Merge "MediaCodec: add DeathNotifier to BatteryNotifier"
diff --git a/include/media/AudioResamplerPublic.h b/include/media/AudioResamplerPublic.h
index 0634741..07d946d 100644
--- a/include/media/AudioResamplerPublic.h
+++ b/include/media/AudioResamplerPublic.h
@@ -34,6 +34,16 @@
// an int32_t of the phase increments, making the resulting sample rate inexact.
#define AUDIO_RESAMPLER_UP_RATIO_MAX 65536
+#define AUDIO_TIMESTRETCH_SPEED_MIN 0.5f
+#define AUDIO_TIMESTRETCH_SPEED_MAX 2.0f
+#define AUDIO_TIMESTRETCH_SPEED_NORMAL 1.0f
+
+#define AUDIO_TIMESTRETCH_PITCH_MIN 0.5f
+#define AUDIO_TIMESTRETCH_PITCH_MAX 2.0f
+#define AUDIO_TIMESTRETCH_PITCH_NORMAL 1.0f
+
+// TODO: Consider putting these inlines into a class scope
+
// Returns the source frames needed to resample to destination frames. This is not a precise
// value and depends on the resampler (and possibly how it handles rounding internally).
// Nevertheless, this should be an upper bound on the requirements of the resampler.
@@ -58,4 +68,13 @@
return dstFrames > 2 ? dstFrames - 2 : 0;
}
+static inline size_t sourceFramesNeededWithTimestretch(
+ uint32_t srcSampleRate, size_t dstFramesRequired, uint32_t dstSampleRate,
+ float speed) {
+ // required is the number of input frames the resampler needs
+ size_t required = sourceFramesNeeded(srcSampleRate, dstFramesRequired, dstSampleRate);
+ // to deliver this, the time stretcher requires:
+ return required * (double)speed + 1 + 1; // accounting for rounding dependencies
+}
+
#endif // ANDROID_AUDIO_RESAMPLER_PUBLIC_H
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index e7e0703..a06197f 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -359,6 +359,21 @@
/* Return current source sample rate in Hz */
uint32_t getSampleRate() const;
+ /* Set source playback rate for timestretch
+ * 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
+ * 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
+ *
+ * AUDIO_TIMESTRETCH_SPEED_MIN <= speed <= AUDIO_TIMESTRETCH_SPEED_MAX
+ * AUDIO_TIMESTRETCH_PITCH_MIN <= pitch <= AUDIO_TIMESTRETCH_PITCH_MAX
+ *
+ * Speed increases the playback rate of media, but does not alter pitch.
+ * Pitch increases the "tonal frequency" of media, but does not affect the playback rate.
+ */
+ status_t setPlaybackRate(float speed, float pitch);
+
+ /* Return current playback rate */
+ void getPlaybackRate(float *speed, float *pitch) const;
+
/* Enables looping and sets the start and end points of looping.
* Only supported for static buffer mode.
*
@@ -719,6 +734,9 @@
// increment mPosition by the delta of mServer, and return new value of mPosition
uint32_t updateAndGetPosition_l();
+ // check sample rate and speed is compatible with AudioTrack
+ bool isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const;
+
// Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -730,6 +748,8 @@
float mVolume[2];
float mSendLevel;
mutable uint32_t mSampleRate; // mutable because getSampleRate() can update it
+ float mSpeed; // timestretch: 1.0f for normal speed.
+ float mPitch; // timestretch: 1.0f for normal pitch.
size_t mFrameCount; // corresponds to current IAudioTrack, value is
// reported back by AudioFlinger to the client
size_t mReqFrameCount; // frame count to request the first or next time
diff --git a/include/media/ICrypto.h b/include/media/ICrypto.h
index ac2b3ba..aa04dbe 100644
--- a/include/media/ICrypto.h
+++ b/include/media/ICrypto.h
@@ -44,6 +44,8 @@
virtual void notifyResolution(uint32_t width, uint32_t height) = 0;
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) = 0;
+
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
@@ -62,6 +64,9 @@
virtual status_t onTransact(
uint32_t code, const Parcel &data, Parcel *reply,
uint32_t flags = 0);
+private:
+ void readVector(const Parcel &data, Vector<uint8_t> &vector) const;
+ void writeVector(Parcel *reply, Vector<uint8_t> const &vector) const;
};
} // namespace android
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index c1483f3..a8d0fcb 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -300,6 +300,7 @@
OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels);
status_t setPriority(int32_t priority);
+ status_t setOperatingRate(float rateFloat, bool isVideo);
status_t setMinBufferSize(OMX_U32 portIndex, size_t size);
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 0786fb9..3e3c276 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -135,6 +135,8 @@
status_t getOutputFormat(sp<AMessage> *format) const;
status_t getInputFormat(sp<AMessage> *format) const;
+ status_t getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const;
+
status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const;
status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const;
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index 5644428..6cc2e2b 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <utils/RefBase.h>
#include <audio_utils/roundup.h>
+#include <media/AudioResamplerPublic.h>
#include <media/SingleStateQueue.h>
namespace android {
@@ -113,6 +114,14 @@
mPosLoopQueue;
};
+
+struct AudioTrackPlaybackRate {
+ float mSpeed;
+ float mPitch;
+};
+
+typedef SingleStateQueue<AudioTrackPlaybackRate> AudioTrackPlaybackRateQueue;
+
// ----------------------------------------------------------------------------
// Important: do not add any virtual methods, including ~
@@ -159,6 +168,8 @@
uint32_t mSampleRate; // AudioTrack only: client's requested sample rate in Hz
// or 0 == default. Write-only client, read-only server.
+ AudioTrackPlaybackRateQueue::Shared mPlaybackRateQueue;
+
// client write-only, server read-only
uint16_t mSendLevel; // Fixed point U4.12 so 0x1000 means 1.0
@@ -313,7 +324,8 @@
AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false)
: ClientProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/,
- clientInServer) { }
+ clientInServer),
+ mPlaybackRateMutator(&cblk->mPlaybackRateQueue) { }
virtual ~AudioTrackClientProxy() { }
// No barriers on the following operations, so the ordering of loads/stores
@@ -333,6 +345,13 @@
mCblk->mSampleRate = sampleRate;
}
+ void setPlaybackRate(float speed, float pitch) {
+ AudioTrackPlaybackRate playbackRate;
+ playbackRate.mSpeed = speed;
+ playbackRate.mPitch = pitch;
+ mPlaybackRateMutator.push(playbackRate);
+ }
+
virtual void flush();
virtual uint32_t getUnderrunFrames() const {
@@ -344,6 +363,9 @@
bool getStreamEndDone() const;
status_t waitStreamEndDone(const struct timespec *requested);
+
+private:
+ AudioTrackPlaybackRateQueue::Mutator mPlaybackRateMutator;
};
class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -458,8 +480,11 @@
public:
AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
size_t frameSize, bool clientInServer = false, uint32_t sampleRate = 0)
- : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) {
+ : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer),
+ mPlaybackRateObserver(&cblk->mPlaybackRateQueue) {
mCblk->mSampleRate = sampleRate;
+ mPlaybackRate.mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+ mPlaybackRate.mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
}
protected:
virtual ~AudioTrackServerProxy() { }
@@ -493,6 +518,13 @@
// Return the total number of frames that AudioFlinger has obtained and released
virtual size_t framesReleased() const { return mCblk->mServer; }
+
+ // Return the playback speed and pitch read atomically. Not multi-thread safe on server side.
+ void getPlaybackRate(float *speed, float *pitch);
+
+private:
+ AudioTrackPlaybackRate mPlaybackRate; // last observed playback rate
+ AudioTrackPlaybackRateQueue::Observer mPlaybackRateObserver;
};
class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 9e9ec5b..89138e2 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -56,6 +56,24 @@
return convertTimespecToUs(tv);
}
+// Must match similar computation in createTrack_l in Threads.cpp.
+// TODO: Move to a common library
+static size_t calculateMinFrameCount(
+ uint32_t afLatencyMs, uint32_t afFrameCount, uint32_t afSampleRate,
+ uint32_t sampleRate, float speed)
+{
+ // Ensure that buffer depth covers at least audio hardware latency
+ uint32_t minBufCount = afLatencyMs / ((1000 * afFrameCount) / afSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ ALOGV("calculateMinFrameCount afLatency %u afFrameCount %u afSampleRate %u "
+ "sampleRate %u speed %f minBufCount: %u",
+ afLatencyMs, afFrameCount, afSampleRate, sampleRate, speed, minBufCount);
+ return minBufCount * sourceFramesNeededWithTimestretch(
+ sampleRate, afFrameCount, afSampleRate, speed);
+}
+
// static
status_t AudioTrack::getMinFrameCount(
size_t* frameCount,
@@ -94,13 +112,10 @@
return status;
}
- // Ensure that buffer depth covers at least audio hardware latency
- uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
- if (minBufCount < 2) {
- minBufCount = 2;
- }
+ // When called from createTrack, speed is 1.0f (normal speed).
+ // This is rechecked again on setting playback rate (TODO: on setting sample rate, too).
+ *frameCount = calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, 1.0f);
- *frameCount = minBufCount * sourceFramesNeeded(sampleRate, afFrameCount, afSampleRate);
// The formula above should always produce a non-zero value under normal circumstances:
// AudioTrack.SAMPLE_RATE_HZ_MIN <= sampleRate <= AudioTrack.SAMPLE_RATE_HZ_MAX.
// Return error in the unlikely event that it does not, as that's part of the API contract.
@@ -109,8 +124,8 @@
streamType, sampleRate);
return BAD_VALUE;
}
- ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, minBufCount=%u, afSampleRate=%u, afLatency=%u",
- *frameCount, afFrameCount, minBufCount, afSampleRate, afLatency);
+ ALOGV("getMinFrameCount=%zu: afFrameCount=%zu, afSampleRate=%u, afLatency=%u",
+ *frameCount, afFrameCount, afSampleRate, afLatency);
return NO_ERROR;
}
@@ -360,6 +375,8 @@
return BAD_VALUE;
}
mSampleRate = sampleRate;
+ mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+ mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
// Make copy of input parameter offloadInfo so that in the future:
// (a) createTrack_l doesn't need it as an input parameter
@@ -689,6 +706,7 @@
if (rate == 0 || rate > afSamplingRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX) {
return BAD_VALUE;
}
+ // TODO: Should we also check if the buffer size is compatible?
mSampleRate = rate;
mProxy->setSampleRate(rate);
@@ -719,6 +737,42 @@
return mSampleRate;
}
+status_t AudioTrack::setPlaybackRate(float speed, float pitch)
+{
+ if (speed < AUDIO_TIMESTRETCH_SPEED_MIN
+ || speed > AUDIO_TIMESTRETCH_SPEED_MAX
+ || pitch < AUDIO_TIMESTRETCH_PITCH_MIN
+ || pitch > AUDIO_TIMESTRETCH_PITCH_MAX) {
+ return BAD_VALUE;
+ }
+ AutoMutex lock(mLock);
+ if (speed == mSpeed && pitch == mPitch) {
+ return NO_ERROR;
+ }
+ if (mIsTimed || isOffloadedOrDirect_l()) {
+ return INVALID_OPERATION;
+ }
+ if (mFlags & AUDIO_OUTPUT_FLAG_FAST) {
+ return INVALID_OPERATION;
+ }
+ // Check if the buffer size is compatible.
+ if (!isSampleRateSpeedAllowed_l(mSampleRate, speed)) {
+ ALOGV("setPlaybackRate(%f, %f) failed", speed, pitch);
+ return BAD_VALUE;
+ }
+ mSpeed = speed;
+ mPitch = pitch;
+ mProxy->setPlaybackRate(speed, pitch);
+ return NO_ERROR;
+}
+
+void AudioTrack::getPlaybackRate(float *speed, float *pitch) const
+{
+ AutoMutex lock(mLock);
+ *speed = mSpeed;
+ *pitch = mPitch;
+}
+
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
@@ -1086,8 +1140,16 @@
// there _is_ a frameCount parameter. We silently ignore it.
frameCount = mSharedBuffer->size() / mFrameSize;
} else {
- // For fast and normal streaming tracks,
- // the frame count calculations and checks are done by server
+ // For fast tracks the frame count calculations and checks are done by server
+
+ if ((mFlags & AUDIO_OUTPUT_FLAG_FAST) == 0) {
+ // for normal tracks precompute the frame count based on speed.
+ const size_t minFrameCount = calculateMinFrameCount(
+ afLatency, afFrameCount, afSampleRate, mSampleRate, mSpeed);
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
}
IAudioFlinger::track_flags_t trackFlags = IAudioFlinger::TRACK_DEFAULT;
@@ -1230,6 +1292,7 @@
}
mAudioTrack->attachAuxEffect(mAuxEffectId);
+ // FIXME doesn't take into account speed or future sample rate changes (until restoreTrack)
// FIXME don't believe this lie
mLatency = afLatency + (1000*frameCount) / mSampleRate;
@@ -1255,6 +1318,7 @@
mProxy->setSendLevel(mSendLevel);
mProxy->setSampleRate(mSampleRate);
+ mProxy->setPlaybackRate(mSpeed, mPitch);
mProxy->setMinimum(mNotificationFramesAct);
mDeathNotifier = new DeathNotifier(this);
@@ -1617,6 +1681,7 @@
// Cache other fields that will be needed soon
uint32_t sampleRate = mSampleRate;
+ float speed = mSpeed;
uint32_t notificationFrames = mNotificationFramesAct;
if (mRefreshRemaining) {
mRefreshRemaining = false;
@@ -1745,7 +1810,7 @@
if (minFrames != (uint32_t) ~0) {
// This "fudge factor" avoids soaking CPU, and compensates for late progress by server
static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
- ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
+ ns = ((double)minFrames * 1000000000) / ((double)sampleRate * speed) + kFudgeNs;
}
// If not supplying data by EVENT_MORE_DATA, then we're done
@@ -1786,7 +1851,8 @@
if (mRetryOnPartialBuffer && !isOffloaded()) {
mRetryOnPartialBuffer = false;
if (avail < mRemainingFrames) {
- int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
+ int64_t myns = ((double)(mRemainingFrames - avail) * 1100000000)
+ / ((double)sampleRate * speed);
if (ns < 0 || myns < ns) {
ns = myns;
}
@@ -1841,7 +1907,7 @@
// that total to a sum == notificationFrames.
if (0 < misalignment && misalignment <= mRemainingFrames) {
mRemainingFrames = misalignment;
- return (mRemainingFrames * 1100000000LL) / sampleRate;
+ return ((double)mRemainingFrames * 1100000000) / ((double)sampleRate * speed);
}
#endif
@@ -1936,6 +2002,41 @@
return mPosition += (uint32_t) delta;
}
+bool AudioTrack::isSampleRateSpeedAllowed_l(uint32_t sampleRate, float speed) const
+{
+ // applicable for mixing tracks only (not offloaded or direct)
+ if (mStaticProxy != 0) {
+ return true; // static tracks do not have issues with buffer sizing.
+ }
+ status_t status;
+ uint32_t afLatency;
+ status = AudioSystem::getLatency(mOutput, &afLatency);
+ if (status != NO_ERROR) {
+ ALOGE("getLatency(%d) failed status %d", mOutput, status);
+ return false;
+ }
+
+ size_t afFrameCount;
+ status = AudioSystem::getFrameCount(mOutput, &afFrameCount);
+ if (status != NO_ERROR) {
+ ALOGE("getFrameCount(output=%d) status %d", mOutput, status);
+ return false;
+ }
+
+ uint32_t afSampleRate;
+ status = AudioSystem::getSamplingRate(mOutput, &afSampleRate);
+ if (status != NO_ERROR) {
+ ALOGE("getSamplingRate(output=%d) status %d", mOutput, status);
+ return false;
+ }
+
+ const size_t minFrameCount =
+ calculateMinFrameCount(afLatency, afFrameCount, afSampleRate, sampleRate, speed);
+ ALOGV("isSampleRateSpeedAllowed_l mFrameCount %zu minFrameCount %zu",
+ mFrameCount, minFrameCount);
+ return mFrameCount >= minFrameCount;
+}
+
status_t AudioTrack::setParameters(const String8& keyValuePairs)
{
AutoMutex lock(mLock);
@@ -2001,7 +2102,8 @@
return WOULD_BLOCK; // stale timestamp time, occurs before start.
}
const int64_t deltaTimeUs = timestampTimeUs - mStartUs;
- const int64_t deltaPositionByUs = timestamp.mPosition * 1000000LL / mSampleRate;
+ const int64_t deltaPositionByUs = (double)timestamp.mPosition * 1000000
+ / ((double)mSampleRate * mSpeed);
if (deltaPositionByUs > deltaTimeUs + kTimeJitterUs) {
// Verify that the counter can't count faster than the sample rate
@@ -2088,7 +2190,8 @@
snprintf(buffer, 255, " format(%d), channel count(%d), frame count(%zu)\n", mFormat,
mChannelCount, mFrameCount);
result.append(buffer);
- snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
+ snprintf(buffer, 255, " sample rate(%u), speed(%f), status(%d)\n",
+ mSampleRate, mSpeed, mStatus);
result.append(buffer);
snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency);
result.append(buffer);
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 6d5f1af..ba67b40 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -793,6 +793,16 @@
(void) android_atomic_or(CBLK_UNDERRUN, &cblk->mFlags);
}
+void AudioTrackServerProxy::getPlaybackRate(float *speed, float *pitch)
+{ // do not call from multiple threads without holding lock
+ AudioTrackPlaybackRate playbackRate;
+ if (mPlaybackRateObserver.poll(playbackRate)) {
+ mPlaybackRate = playbackRate;
+ }
+ *speed = mPlaybackRate.mSpeed;
+ *pitch = mPlaybackRate.mPitch;
+}
+
// ---------------------------------------------------------------------------
StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
diff --git a/media/libmedia/ICrypto.cpp b/media/libmedia/ICrypto.cpp
index 23308c1..9246a7c 100644
--- a/media/libmedia/ICrypto.cpp
+++ b/media/libmedia/ICrypto.cpp
@@ -35,6 +35,7 @@
REQUIRES_SECURE_COMPONENT,
DECRYPT,
NOTIFY_RESOLUTION,
+ SET_MEDIADRM_SESSION,
};
struct BpCrypto : public BpInterface<ICrypto> {
@@ -161,7 +162,28 @@
remote()->transact(NOTIFY_RESOLUTION, data, &reply);
}
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ Parcel data, reply;
+ data.writeInterfaceToken(ICrypto::getInterfaceDescriptor());
+
+ writeVector(data, sessionId);
+ remote()->transact(SET_MEDIADRM_SESSION, data, &reply);
+
+ return reply.readInt32();
+ }
+
private:
+ void readVector(Parcel &reply, Vector<uint8_t> &vector) const {
+ uint32_t size = reply.readInt32();
+ vector.insertAt((size_t)0, size);
+ reply.read(vector.editArray(), size);
+ }
+
+ void writeVector(Parcel &data, Vector<uint8_t> const &vector) const {
+ data.writeInt32(vector.size());
+ data.write(vector.array(), vector.size());
+ }
+
DISALLOW_EVIL_CONSTRUCTORS(BpCrypto);
};
@@ -169,6 +191,17 @@
////////////////////////////////////////////////////////////////////////////////
+void BnCrypto::readVector(const Parcel &data, Vector<uint8_t> &vector) const {
+ uint32_t size = data.readInt32();
+ vector.insertAt((size_t)0, size);
+ data.read(vector.editArray(), size);
+}
+
+void BnCrypto::writeVector(Parcel *reply, Vector<uint8_t> const &vector) const {
+ reply->writeInt32(vector.size());
+ reply->write(vector.array(), vector.size());
+}
+
status_t BnCrypto::onTransact(
uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
switch (code) {
@@ -317,6 +350,15 @@
return OK;
}
+ case SET_MEDIADRM_SESSION:
+ {
+ CHECK_INTERFACE(IDrm, data, reply);
+ Vector<uint8_t> sessionId;
+ readVector(data, sessionId);
+ reply->writeInt32(setMediaDrmSession(sessionId));
+ return OK;
+ }
+
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmediaplayerservice/Crypto.cpp b/media/libmediaplayerservice/Crypto.cpp
index e768772..f639193 100644
--- a/media/libmediaplayerservice/Crypto.cpp
+++ b/media/libmediaplayerservice/Crypto.cpp
@@ -268,4 +268,14 @@
}
}
+status_t Crypto::setMediaDrmSession(const Vector<uint8_t> &sessionId) {
+ Mutex::Autolock autoLock(mLock);
+
+ status_t result = NO_INIT;
+ if (mInitCheck == OK && mPlugin != NULL) {
+ result = mPlugin->setMediaDrmSession(sessionId);
+ }
+ return result;
+}
+
} // namespace android
diff --git a/media/libmediaplayerservice/Crypto.h b/media/libmediaplayerservice/Crypto.h
index d5f3c50..99ea95d 100644
--- a/media/libmediaplayerservice/Crypto.h
+++ b/media/libmediaplayerservice/Crypto.h
@@ -47,6 +47,8 @@
virtual void notifyResolution(uint32_t width, uint32_t height);
+ virtual status_t setMediaDrmSession(const Vector<uint8_t> &sessionId);
+
virtual ssize_t decrypt(
bool secure,
const uint8_t key[16],
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 04ac699..3fff1e6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -82,25 +82,69 @@
switch (msg->what()) {
case kWhatCodecNotify:
{
- if (!isStaleReply(msg)) {
- int32_t numInput, numOutput;
-
- if (!msg->findInt32("input-buffers", &numInput)) {
- numInput = INT32_MAX;
- }
-
- if (!msg->findInt32("output-buffers", &numOutput)) {
- numOutput = INT32_MAX;
- }
-
- if (!mPaused) {
- while (numInput-- > 0 && handleAnInputBuffer()) {}
- }
-
- while (numOutput-- > 0 && handleAnOutputBuffer()) {}
+ if (mPaused) {
+ break;
}
- requestCodecNotification();
+ int32_t cbID;
+ CHECK(msg->findInt32("callbackID", &cbID));
+
+ ALOGV("kWhatCodecNotify: cbID = %d", cbID);
+ switch (cbID) {
+ case MediaCodec::CB_INPUT_AVAILABLE:
+ {
+ int32_t index;
+ CHECK(msg->findInt32("index", &index));
+
+ handleAnInputBuffer(index);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_AVAILABLE:
+ {
+ int32_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ int32_t flags;
+
+ CHECK(msg->findInt32("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", &flags));
+
+ handleAnOutputBuffer(index, offset, size, timeUs, flags);
+ break;
+ }
+
+ case MediaCodec::CB_OUTPUT_FORMAT_CHANGED:
+ {
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+
+ handleOutputFormatChange(format);
+ break;
+ }
+
+ case MediaCodec::CB_ERROR:
+ {
+ status_t err;
+ CHECK(msg->findInt32("err", &err));
+ ALOGE("Decoder (%s) reported error : 0x%x",
+ mIsAudio ? "audio" : "video", err);
+
+ handleError(err);
+ break;
+ }
+
+ default:
+ {
+ TRESPASS();
+ break;
+ }
+ }
+
break;
}
@@ -188,6 +232,9 @@
CHECK_EQ((status_t)OK, mCodec->getOutputFormat(&mOutputFormat));
CHECK_EQ((status_t)OK, mCodec->getInputFormat(&mInputFormat));
+ sp<AMessage> reply = new AMessage(kWhatCodecNotify, this);
+ mCodec->setCallback(reply);
+
err = mCodec->start();
if (err != OK) {
ALOGE("Failed to start %s decoder (err=%d)", mComponentName.c_str(), err);
@@ -197,18 +244,8 @@
return;
}
- // the following should work after start
- CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
releaseAndResetMediaBuffers();
- CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
- ALOGV("[%s] got %zu input and %zu output buffers",
- mComponentName.c_str(),
- mInputBuffers.size(),
- mOutputBuffers.size());
- if (mRenderer != NULL) {
- requestCodecNotification();
- }
mPaused = false;
mResumePending = false;
}
@@ -217,16 +254,14 @@
bool hadNoRenderer = (mRenderer == NULL);
mRenderer = renderer;
if (hadNoRenderer && mRenderer != NULL) {
- requestCodecNotification();
+ // this means that the widevine legacy source is ready
+ onRequestInputBuffers();
}
}
void NuPlayer::Decoder::onGetInputBuffers(
Vector<sp<ABuffer> > *dstBuffers) {
- dstBuffers->clear();
- for (size_t i = 0; i < mInputBuffers.size(); i++) {
- dstBuffers->push(mInputBuffers[i]);
- }
+ CHECK_EQ((status_t)OK, mCodec->getWidevineLegacyBuffers(dstBuffers));
}
void NuPlayer::Decoder::onResume(bool notifyComplete) {
@@ -235,6 +270,7 @@
if (notifyComplete) {
mResumePending = true;
}
+ mCodec->start();
}
void NuPlayer::Decoder::doFlush(bool notifyComplete) {
@@ -261,8 +297,10 @@
// we attempt to release the buffers even if flush fails.
}
releaseAndResetMediaBuffers();
+ mPaused = true;
}
+
void NuPlayer::Decoder::onFlush() {
doFlush(true);
@@ -276,7 +314,6 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
- mPaused = true;
}
void NuPlayer::Decoder::onShutdown(bool notifyComplete) {
@@ -320,7 +357,9 @@
}
void NuPlayer::Decoder::doRequestBuffers() {
- if (isDiscontinuityPending()) {
+ // mRenderer is only NULL if we have a legacy widevine source that
+ // is not yet ready. In this case we must not fetch input.
+ if (isDiscontinuityPending() || mRenderer == NULL) {
return;
}
status_t err = OK;
@@ -347,34 +386,50 @@
}
}
-bool NuPlayer::Decoder::handleAnInputBuffer() {
+void NuPlayer::Decoder::handleError(int32_t err)
+{
+ // We cannot immediately release the codec due to buffers still outstanding
+ // in the renderer. We signal to the player the error so it can shutdown/release the
+ // decoder after flushing and increment the generation to discard unnecessary messages.
+
+ ++mBufferGeneration;
+
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatError);
+ notify->setInt32("err", err);
+ notify->post();
+}
+
+bool NuPlayer::Decoder::handleAnInputBuffer(size_t index) {
if (isDiscontinuityPending()) {
return false;
}
- size_t bufferIx = -1;
- status_t res = mCodec->dequeueInputBuffer(&bufferIx);
- ALOGV("[%s] dequeued input: %d",
- mComponentName.c_str(), res == OK ? (int)bufferIx : res);
- if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue input buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
+
+ sp<ABuffer> buffer;
+ mCodec->getInputBuffer(index, &buffer);
+
+ if (index >= mInputBuffers.size()) {
+ for (size_t i = mInputBuffers.size(); i <= index; ++i) {
+ mInputBuffers.add();
+ mMediaBuffers.add();
+ mInputBufferIsDequeued.add();
+ mMediaBuffers.editItemAt(i) = NULL;
+ mInputBufferIsDequeued.editItemAt(i) = false;
}
- return false;
}
+ mInputBuffers.editItemAt(index) = buffer;
- CHECK_LT(bufferIx, mInputBuffers.size());
+ //CHECK_LT(bufferIx, mInputBuffers.size());
- if (mMediaBuffers[bufferIx] != NULL) {
- mMediaBuffers[bufferIx]->release();
- mMediaBuffers.editItemAt(bufferIx) = NULL;
+ if (mMediaBuffers[index] != NULL) {
+ mMediaBuffers[index]->release();
+ mMediaBuffers.editItemAt(index) = NULL;
}
- mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+ mInputBufferIsDequeued.editItemAt(index) = true;
if (!mCSDsToSubmit.isEmpty()) {
sp<AMessage> msg = new AMessage();
- msg->setSize("buffer-ix", bufferIx);
+ msg->setSize("buffer-ix", index);
sp<ABuffer> buffer = mCSDsToSubmit.itemAt(0);
ALOGI("[%s] resubmitting CSD", mComponentName.c_str());
@@ -392,94 +447,38 @@
mPendingInputMessages.erase(mPendingInputMessages.begin());
}
- if (!mInputBufferIsDequeued.editItemAt(bufferIx)) {
+ if (!mInputBufferIsDequeued.editItemAt(index)) {
return true;
}
- mDequeuedInputBuffers.push_back(bufferIx);
+ mDequeuedInputBuffers.push_back(index);
onRequestInputBuffers();
return true;
}
-bool NuPlayer::Decoder::handleAnOutputBuffer() {
- size_t bufferIx = -1;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
- status_t res = mCodec->dequeueOutputBuffer(
- &bufferIx, &offset, &size, &timeUs, &flags);
-
- if (res != OK) {
- ALOGV("[%s] dequeued output: %d", mComponentName.c_str(), res);
- } else {
- ALOGV("[%s] dequeued output: %d (time=%lld flags=%" PRIu32 ")",
- mComponentName.c_str(), (int)bufferIx, timeUs, flags);
- }
-
- if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
- res = mCodec->getOutputBuffers(&mOutputBuffers);
- if (res != OK) {
- ALOGE("Failed to get output buffers for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- // NuPlayer ignores this
- return true;
- } else if (res == INFO_FORMAT_CHANGED) {
- sp<AMessage> format = new AMessage();
- res = mCodec->getOutputFormat(&format);
- if (res != OK) {
- ALOGE("Failed to get output format for %s after INFO event (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
-
- if (!mIsAudio) {
- sp<AMessage> notify = mNotify->dup();
- notify->setInt32("what", kWhatVideoSizeChanged);
- notify->setMessage("format", format);
- notify->post();
- } else if (mRenderer != NULL) {
- uint32_t flags;
- int64_t durationUs;
- bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
- if (!hasVideo &&
- mSource->getDuration(&durationUs) == OK &&
- durationUs
- > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
- flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
- } else {
- flags = AUDIO_OUTPUT_FLAG_NONE;
- }
-
- res = mRenderer->openAudioSink(
- format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaded */);
- if (res != OK) {
- ALOGE("Failed to open AudioSink on format change for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- return false;
- }
- }
- return true;
- } else if (res == INFO_DISCONTINUITY) {
- // nothing to do
- return true;
- } else if (res != OK) {
- if (res != -EAGAIN) {
- ALOGE("Failed to dequeue output buffer for %s (err=%d)",
- mComponentName.c_str(), res);
- handleError(res);
- }
+bool NuPlayer::Decoder::handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags) {
+ if (mFormatChangePending) {
return false;
}
- CHECK_LT(bufferIx, mOutputBuffers.size());
- sp<ABuffer> buffer = mOutputBuffers[bufferIx];
+// CHECK_LT(bufferIx, mOutputBuffers.size());
+ sp<ABuffer> buffer;
+ mCodec->getOutputBuffer(index, &buffer);
+
+ if (index >= mOutputBuffers.size()) {
+ for (size_t i = mOutputBuffers.size(); i <= index; ++i) {
+ mOutputBuffers.add();
+ }
+ }
+
+ mOutputBuffers.editItemAt(index) = buffer;
+
buffer->setRange(offset, size);
buffer->meta()->clear();
buffer->meta()->setInt64("timeUs", timeUs);
@@ -488,7 +487,7 @@
// we do not expect CODECCONFIG or SYNCFRAME for decoder
sp<AMessage> reply = new AMessage(kWhatRenderBuffer, this);
- reply->setSize("buffer-ix", bufferIx);
+ reply->setSize("buffer-ix", index);
reply->setInt32("generation", mBufferGeneration);
if (eos) {
@@ -522,6 +521,29 @@
return true;
}
+void NuPlayer::Decoder::handleOutputFormatChange(const sp<AMessage> &format) {
+ if (!mIsAudio) {
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", kWhatVideoSizeChanged);
+ notify->setMessage("format", format);
+ notify->post();
+ } else if (mRenderer != NULL) {
+ uint32_t flags;
+ int64_t durationUs;
+ bool hasVideo = (mSource->getFormat(false /* audio */) != NULL);
+ if (!hasVideo &&
+ mSource->getDuration(&durationUs) == OK &&
+ durationUs > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) {
+ flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+ } else {
+ flags = AUDIO_OUTPUT_FLAG_NONE;
+ }
+
+ mRenderer->openAudioSink(
+ format, false /* offloadOnly */, hasVideo, flags, NULL /* isOffloaed */);
+ }
+}
+
void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
for (size_t i = 0; i < mMediaBuffers.size(); i++) {
if (mMediaBuffers[i] != NULL) {
@@ -825,7 +847,8 @@
mPaused = true;
} else if (mTimeChangePending) {
if (flushOnTimeChange) {
- doFlush(false /*notifyComplete*/);
+ doFlush(false /* notifyComplete */);
+ signalResume(false /* notifyComplete */);
}
// restart fetching input
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 4aab2c6..0c0e90c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -87,8 +87,15 @@
bool mResumePending;
AString mComponentName;
- bool handleAnInputBuffer();
- bool handleAnOutputBuffer();
+ void handleError(int32_t err);
+ bool handleAnInputBuffer(size_t index);
+ bool handleAnOutputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t timeUs,
+ int32_t flags);
+ void handleOutputFormatChange(const sp<AMessage> &format);
void releaseAndResetMediaBuffers();
void requestCodecNotification();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index a2ec51c..827bdc1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -312,6 +312,9 @@
int64_t delayUs =
mAudioSink->msecsPerFrame()
* numFramesPendingPlayout * 1000ll;
+ if (mPlaybackRate > 1.0f) {
+ delayUs /= mPlaybackRate;
+ }
// Let's give it more data after about half that time
// has elapsed.
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 97f3e20..45f6339 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1685,6 +1685,16 @@
err = setPriority(priority);
}
+ int32_t rateInt = -1;
+ float rateFloat = -1;
+ if (!msg->findFloat("operating-rate", &rateFloat)) {
+ msg->findInt32("operating-rate", &rateInt);
+ rateFloat = (float)rateInt; // 16MHz (FLINTMAX) is OK for upper bound.
+ }
+ if (rateFloat > 0) {
+ err = setOperatingRate(rateFloat, video);
+ }
+
mBaseOutputFormat = outputFormat;
CHECK_EQ(getPortFormat(kPortIndexInput, inputFormat), (status_t)OK);
@@ -1711,6 +1721,34 @@
return OK;
}
+status_t ACodec::setOperatingRate(float rateFloat, bool isVideo) {
+ if (rateFloat < 0) {
+ return BAD_VALUE;
+ }
+ OMX_U32 rate;
+ if (isVideo) {
+ if (rateFloat > 65535) {
+ return BAD_VALUE;
+ }
+ rate = (OMX_U32)(rateFloat * 65536.0f + 0.5f);
+ } else {
+ if (rateFloat > UINT_MAX) {
+ return BAD_VALUE;
+ }
+ rate = (OMX_U32)(rateFloat);
+ }
+ OMX_PARAM_U32TYPE config;
+ InitOMXParams(&config);
+ config.nU32 = rate;
+ status_t err = mOMX->setConfig(
+ mNode, (OMX_INDEXTYPE)OMX_IndexConfigOperatingRate,
+ &config, sizeof(config));
+ if (err != OK) {
+ ALOGI("codec does not support config operating rate (err %d)", err);
+ }
+ return OK;
+}
+
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
@@ -4902,6 +4940,7 @@
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node = NULL;
+ status_t err = OMX_ErrorComponentNotFound;
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).mName.string();
@@ -4910,7 +4949,7 @@
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
- status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+ err = omx->allocateNode(componentName.c_str(), observer, &node);
androidSetThreadPriority(tid, prevPriority);
if (err == OK) {
@@ -4924,13 +4963,13 @@
if (node == NULL) {
if (!mime.empty()) {
- ALOGE("Unable to instantiate a %scoder for type '%s'.",
- encoder ? "en" : "de", mime.c_str());
+ ALOGE("Unable to instantiate a %scoder for type '%s' with err %#x.",
+ encoder ? "en" : "de", mime.c_str(), err);
} else {
- ALOGE("Unable to instantiate codec '%s'.", componentName.c_str());
+ ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);
}
- mCodec->signalError(OMX_ErrorComponentNotFound);
+ mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));
return false;
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 55548f8..5538cb0 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -610,6 +610,16 @@
return OK;
}
+status_t MediaCodec::getWidevineLegacyBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
+ msg->setInt32("portIndex", kPortIndexInput);
+ msg->setPointer("buffers", buffers);
+ msg->setInt32("widevine", true);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
msg->setInt32("portIndex", kPortIndexInput);
@@ -1666,8 +1676,12 @@
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ // Unfortunately widevine legacy source requires knowing all of the
+ // codec input buffers, so we have to provide them even in async mode.
+ int32_t widevine = 0;
+ msg->findInt32("widevine", &widevine);
- if (!isExecuting() || (mFlags & kFlagIsAsync)) {
+ if (!isExecuting() || ((mFlags & kFlagIsAsync) && !widevine)) {
PostReplyWithError(replyID, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index b6fa810..6568d25 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -399,6 +399,9 @@
ALOGV("output format is '%s'", mOutputFormat->debugString(0).c_str());
+ mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
+ mEncoder->setCallback(mEncoderActivityNotify);
+
status_t err = mEncoder->configure(
mOutputFormat,
NULL /* nativeWindow */,
@@ -422,9 +425,6 @@
}
}
- mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, mReflector);
- mEncoder->setCallback(mEncoderActivityNotify);
-
err = mEncoder->start();
if (err != OK) {
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 26f8da1..74f58e9 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -1455,6 +1455,10 @@
if (bandwidthIndex >= 0) {
mOrigBandwidthIndex = mCurBandwidthIndex;
mCurBandwidthIndex = bandwidthIndex;
+ if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+ ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
+ mOrigBandwidthIndex, mCurBandwidthIndex);
+ }
}
CHECK_LT(mCurBandwidthIndex, mBandwidthItems.size());
const BandwidthItem &item = mBandwidthItems.itemAt(mCurBandwidthIndex);
@@ -1574,6 +1578,7 @@
if (timeUs >= 0) {
mLastSeekTimeUs = timeUs;
+ mLastDequeuedTimeUs = timeUs;
for (size_t i = 0; i < mPacketSources.size(); i++) {
mPacketSources.editValueAt(i)->clear();
@@ -1626,8 +1631,10 @@
ALOGV("stream %zu changed: oldURI %s, newURI %s", i,
mStreams[i].mUri.c_str(), URIs[i].c_str());
sp<AnotherPacketSource> source = mPacketSources.valueFor(indexToType(i));
- source->queueDiscontinuity(
- ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ if (source->getLatestDequeuedMeta() != NULL) {
+ source->queueDiscontinuity(
+ ATSParser::DISCONTINUITY_FORMATCHANGE, NULL, true);
+ }
}
// Determine which decoders to shutdown on the player side,
// a decoder has to be shutdown if its streamtype was active
@@ -1687,10 +1694,6 @@
// and resume audio.
mSwapMask = mNewStreamMask & mStreamMask & ~resumeMask;
switching = (mSwapMask != 0);
- if (!switching) {
- ALOGV("#### Finishing Bandwidth Switch Early: %zd => %zd",
- mOrigBandwidthIndex, mCurBandwidthIndex);
- }
}
mRealTimeBaseUs = ALooper::GetNowUs() - mLastDequeuedTimeUs;
} else {
@@ -1843,7 +1846,11 @@
mSwitchInProgress = true;
} else {
mStreamMask = mNewStreamMask;
- mOrigBandwidthIndex = mCurBandwidthIndex;
+ if (mOrigBandwidthIndex != mCurBandwidthIndex) {
+ ALOGV("#### Finished Bandwidth Switch Early: %zd => %zd",
+ mOrigBandwidthIndex, mCurBandwidthIndex);
+ mOrigBandwidthIndex = mCurBandwidthIndex;
+ }
}
ALOGV("onChangeConfiguration3: mSwitchInProgress %d, mStreamMask 0x%x",
@@ -1970,11 +1977,19 @@
bool underflow, ready, down, up;
if (checkBuffering(underflow, ready, down, up)) {
- if (mInPreparationPhase && ready) {
- postPrepared(OK);
+ if (mInPreparationPhase) {
+ // Allow down switch even if we're still preparing.
+ //
+ // Some streams have a high bandwidth index as default,
+ // when bandwidth is low, it takes a long time to buffer
+ // to ready mark, then it immediately pauses after start
+ // as we have to do a down switch. It's better experience
+ // to restart from a lower index, if we detect low bw.
+ if (!switchBandwidthIfNeeded(false /* up */, down) && ready) {
+ postPrepared(OK);
+ }
}
- // don't switch before we report prepared
if (!mInPreparationPhase) {
if (ready) {
stopBufferingIfNecessary();
@@ -1982,8 +1997,7 @@
startBufferingIfNecessary();
}
switchBandwidthIfNeeded(up, down);
- }
-
+ }
}
schedulePollBuffering();
@@ -2075,7 +2089,8 @@
if (mPacketSources[i]->isFinished(0 /* duration */)) {
percent = 100;
} else {
- percent = (int32_t)(100.0 * (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
+ percent = (int32_t)(100.0 *
+ (mLastDequeuedTimeUs + bufferedDurationUs) / durationUs);
}
if (minBufferPercent < 0 || percent < minBufferPercent) {
minBufferPercent = percent;
@@ -2158,10 +2173,14 @@
notify->post();
}
-void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
+/*
+ * returns true if a bandwidth switch is actually needed (and started),
+ * returns false otherwise
+ */
+bool LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
// no need to check bandwidth if we only have 1 bandwidth settings
if (mSwitchInProgress || mBandwidthItems.size() < 2) {
- return;
+ return false;
}
int32_t bandwidthBps;
@@ -2170,7 +2189,7 @@
mLastBandwidthBps = bandwidthBps;
} else {
ALOGV("no bandwidth estimate.");
- return;
+ return false;
}
int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
@@ -2189,16 +2208,16 @@
// bandwidthIndex is < mCurBandwidthIndex, as getBandwidthIndex() only uses 70%
// of measured bw. In that case we don't want to do anything, since we have
// both enough buffer and enough bw.
- if (bandwidthIndex == mCurBandwidthIndex
- || (canSwitchUp && bandwidthIndex < mCurBandwidthIndex)
- || (canSwithDown && bandwidthIndex > mCurBandwidthIndex)) {
- return;
+ if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
+ || (canSwithDown && bandwidthIndex < mCurBandwidthIndex)) {
+ // if not yet prepared, just restart again with new bw index.
+ // this is faster and playback experience is cleaner.
+ changeConfiguration(
+ mInPreparationPhase ? 0 : -1ll, bandwidthIndex);
+ return true;
}
-
- ALOGI("#### Starting Bandwidth Switch: %zd => %zd",
- mCurBandwidthIndex, bandwidthIndex);
- changeConfiguration(-1, bandwidthIndex, false);
}
+ return false;
}
void LiveSession::postError(status_t err) {
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index c587f40..9117bb1 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -292,7 +292,7 @@
bool checkSwitchProgress(
sp<AMessage> &msg, int64_t delayUs, bool *needResumeUntil);
- void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
+ bool switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
void schedulePollBuffering();
void cancelPollBuffering();
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 0676a33..c7912c0 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -355,10 +355,15 @@
int64_t time2 = -1;
int64_t durationUs = 0;
- List<sp<ABuffer> >::iterator it = mBuffers.begin();
- while (it != mBuffers.end()) {
+ List<sp<ABuffer> >::iterator it;
+ for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
const sp<ABuffer> &buffer = *it;
+ int32_t discard;
+ if (buffer->meta()->findInt32("discard", &discard) && discard) {
+ continue;
+ }
+
int64_t timeUs;
if (buffer->meta()->findInt64("timeUs", &timeUs)) {
if (time1 < 0 || timeUs < time1) {
@@ -373,8 +378,6 @@
durationUs += time2 - time1;
time1 = time2 = -1;
}
-
- ++it;
}
return durationUs + (time2 - time1);
@@ -393,11 +396,19 @@
return getBufferedDurationUs_l(&finalResult);
}
- List<sp<ABuffer> >::iterator it = mBuffers.begin();
- sp<ABuffer> buffer = *it;
+ sp<ABuffer> buffer;
+ int32_t discard;
+ int64_t startTimeUs = -1ll;
+ List<sp<ABuffer> >::iterator it;
+ for (it = mBuffers.begin(); it != mBuffers.end(); it++) {
+ buffer = *it;
+ if (buffer->meta()->findInt32("discard", &discard) && discard) {
+ continue;
+ }
+ buffer->meta()->findInt64("timeUs", &startTimeUs);
+ break;
+ }
- int64_t startTimeUs;
- buffer->meta()->findInt64("timeUs", &startTimeUs);
if (startTimeUs < 0) {
return 0;
}
diff --git a/services/audioflinger/AudioHwDevice.cpp b/services/audioflinger/AudioHwDevice.cpp
index 09d86ea..3191598 100644
--- a/services/audioflinger/AudioHwDevice.cpp
+++ b/services/audioflinger/AudioHwDevice.cpp
@@ -44,7 +44,7 @@
AudioStreamOut *outputStream = new AudioStreamOut(this, flags);
// Try to open the HAL first using the current format.
- ALOGV("AudioHwDevice::openOutputStream(), try "
+ ALOGV("openOutputStream(), try "
" sampleRate %d, Format %#x, "
"channelMask %#x",
config->sample_rate,
@@ -59,7 +59,7 @@
// FIXME Look at any modification to the config.
// The HAL might modify the config to suggest a wrapped format.
// Log this so we can see what the HALs are doing.
- ALOGI("AudioHwDevice::openOutputStream(), HAL returned"
+ ALOGI("openOutputStream(), HAL returned"
" sampleRate %d, Format %#x, "
"channelMask %#x, status %d",
config->sample_rate,
@@ -72,16 +72,19 @@
&& ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0)
&& ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0);
- // FIXME - Add isEncodingSupported() query to SPDIF wrapper then
- // call it from here.
if (wrapperNeeded) {
- outputStream = new SpdifStreamOut(this, flags);
- status = outputStream->open(handle, devices, &originalConfig, address);
- if (status != NO_ERROR) {
- ALOGE("ERROR - AudioHwDevice::openOutputStream(), SPDIF open returned %d",
- status);
- delete outputStream;
- outputStream = NULL;
+ if (SPDIFEncoder::isFormatSupported(originalConfig.format)) {
+ outputStream = new SpdifStreamOut(this, flags, originalConfig.format);
+ status = outputStream->open(handle, devices, &originalConfig, address);
+ if (status != NO_ERROR) {
+ ALOGE("ERROR - openOutputStream(), SPDIF open returned %d",
+ status);
+ delete outputStream;
+ outputStream = NULL;
+ }
+ } else {
+ ALOGE("ERROR - openOutputStream(), SPDIFEncoder does not support format 0x%08x",
+ originalConfig.format);
}
}
}
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index cb90ece..c2c791f 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -123,6 +123,7 @@
t->resampler = NULL;
t->downmixerBufferProvider = NULL;
t->mReformatBufferProvider = NULL;
+ t->mTimestretchBufferProvider = NULL;
t++;
}
@@ -135,6 +136,7 @@
delete t->resampler;
delete t->downmixerBufferProvider;
delete t->mReformatBufferProvider;
+ delete t->mTimestretchBufferProvider;
t++;
}
delete [] mState.outputTemp;
@@ -213,6 +215,7 @@
t->mReformatBufferProvider = NULL;
t->downmixerBufferProvider = NULL;
t->mPostDownmixReformatBufferProvider = NULL;
+ t->mTimestretchBufferProvider = NULL;
t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
t->mFormat = format;
t->mMixerInFormat = selectMixerInFormat(format);
@@ -220,6 +223,8 @@
t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
+ t->mSpeed = AUDIO_TIMESTRETCH_SPEED_NORMAL;
+ t->mPitch = AUDIO_TIMESTRETCH_PITCH_NORMAL;
// Check the downmixing (or upmixing) requirements.
status_t status = t->prepareForDownmix();
if (status != OK) {
@@ -412,6 +417,10 @@
mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
bufferProvider = mPostDownmixReformatBufferProvider;
}
+ if (mTimestretchBufferProvider) {
+ mTimestretchBufferProvider->setBufferProvider(bufferProvider);
+ bufferProvider = mTimestretchBufferProvider;
+ }
}
void AudioMixer::deleteTrackName(int name)
@@ -432,7 +441,9 @@
mState.tracks[name].unprepareForDownmix();
// delete the reformatter
mState.tracks[name].unprepareForReformat();
-
+ // delete the timestretch provider
+ delete track.mTimestretchBufferProvider;
+ track.mTimestretchBufferProvider = NULL;
mTrackNames &= ~(1<<name);
}
@@ -654,6 +665,26 @@
}
}
break;
+ case TIMESTRETCH:
+ switch (param) {
+ case PLAYBACK_RATE: {
+ const float speed = reinterpret_cast<float*>(value)[0];
+ const float pitch = reinterpret_cast<float*>(value)[1];
+ ALOG_ASSERT(AUDIO_TIMESTRETCH_SPEED_MIN <= speed
+ && speed <= AUDIO_TIMESTRETCH_SPEED_MAX,
+ "bad speed %f", speed);
+ ALOG_ASSERT(AUDIO_TIMESTRETCH_PITCH_MIN <= pitch
+ && pitch <= AUDIO_TIMESTRETCH_PITCH_MAX,
+ "bad pitch %f", pitch);
+ if (track.setPlaybackRate(speed, pitch)) {
+ ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, %f %f", speed, pitch);
+ // invalidateState(1 << name);
+ }
+ } break;
+ default:
+ LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
+ }
+ break;
default:
LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
@@ -699,6 +730,28 @@
return false;
}
+bool AudioMixer::track_t::setPlaybackRate(float speed, float pitch)
+{
+ if (speed == mSpeed && pitch == mPitch) {
+ return false;
+ }
+ mSpeed = speed;
+ mPitch = pitch;
+ if (mTimestretchBufferProvider == NULL) {
+ // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
+ // but if none exists, it is the channel count (1 for mono).
+ const int timestretchChannelCount = downmixerBufferProvider != NULL
+ ? mMixerChannelCount : channelCount;
+ mTimestretchBufferProvider = new TimestretchBufferProvider(timestretchChannelCount,
+ mMixerInFormat, sampleRate, speed, pitch);
+ reconfigureBufferProviders();
+ } else {
+ reinterpret_cast<TimestretchBufferProvider*>(mTimestretchBufferProvider)
+ ->setPlaybackRate(speed, pitch);
+ }
+ return true;
+}
+
/* Checks to see if the volume ramp has completed and clears the increment
* variables appropriately.
*
@@ -777,6 +830,8 @@
mState.tracks[name].downmixerBufferProvider->reset();
} else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) {
mState.tracks[name].mPostDownmixReformatBufferProvider->reset();
+ } else if (mState.tracks[name].mTimestretchBufferProvider != NULL) {
+ mState.tracks[name].mTimestretchBufferProvider->reset();
}
mState.tracks[name].mInputBufferProvider = bufferProvider;
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index e283b83..e27a0d1 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -73,6 +73,7 @@
RESAMPLE = 0x3001,
RAMP_VOLUME = 0x3002, // ramp to new volume
VOLUME = 0x3003, // don't ramp
+ TIMESTRETCH = 0x3004,
// set Parameter names
// for target TRACK
@@ -100,6 +101,9 @@
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
AUXLEVEL = 0x4210,
+ // for target TIMESTRETCH
+ PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name;
+ // parameter 'value' is a pointer to the new playback rate.
};
@@ -214,6 +218,9 @@
/* Buffer providers are constructed to translate the track input data as needed.
*
+ * TODO: perhaps make a single PlaybackConverterProvider class to move
+ * all pre-mixer track buffer conversions outside the AudioMixer class.
+ *
* 1) mInputBufferProvider: The AudioTrack buffer provider.
* 2) mReformatBufferProvider: If not NULL, performs the audio reformat to
* match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer
@@ -223,11 +230,13 @@
* the number of channels required by the mixer sink.
* 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from
* the downmixer requirements to the mixer engine input requirements.
+ * 5) mTimestretchBufferProvider: Adds timestretching for playback rate
*/
AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
PassthruBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting.
PassthruBufferProvider* downmixerBufferProvider; // wrapper for channel conversion.
PassthruBufferProvider* mPostDownmixReformatBufferProvider;
+ PassthruBufferProvider* mTimestretchBufferProvider;
int32_t sessionId;
@@ -250,6 +259,9 @@
audio_channel_mask_t mMixerChannelMask;
uint32_t mMixerChannelCount;
+ float mSpeed;
+ float mPitch;
+
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; }
@@ -262,6 +274,7 @@
void unprepareForDownmix();
status_t prepareForReformat();
void unprepareForReformat();
+ bool setPlaybackRate(float speed, float pitch);
void reconfigureBufferProviders();
};
diff --git a/services/audioflinger/BufferProviders.cpp b/services/audioflinger/BufferProviders.cpp
index e143805..e058e6c 100644
--- a/services/audioflinger/BufferProviders.cpp
+++ b/services/audioflinger/BufferProviders.cpp
@@ -20,7 +20,9 @@
#include <audio_effects/effect_downmix.h>
#include <audio_utils/primitives.h>
#include <audio_utils/format.h>
+#include <media/AudioResamplerPublic.h>
#include <media/EffectsFactoryApi.h>
+
#include <utils/Log.h>
#include "Configuration.h"
@@ -358,5 +360,165 @@
memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
}
+TimestretchBufferProvider::TimestretchBufferProvider(int32_t channelCount,
+ audio_format_t format, uint32_t sampleRate, float speed, float pitch) :
+ mChannelCount(channelCount),
+ mFormat(format),
+ mSampleRate(sampleRate),
+ mFrameSize(channelCount * audio_bytes_per_sample(format)),
+ mSpeed(speed),
+ mPitch(pitch),
+ mLocalBufferFrameCount(0),
+ mLocalBufferData(NULL),
+ mRemaining(0)
+{
+ ALOGV("TimestretchBufferProvider(%p)(%u, %#x, %u %f %f)",
+ this, channelCount, format, sampleRate, speed, pitch);
+ mBuffer.frameCount = 0;
+}
+
+TimestretchBufferProvider::~TimestretchBufferProvider()
+{
+ ALOGV("~TimestretchBufferProvider(%p)", this);
+ if (mBuffer.frameCount != 0) {
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+ }
+ free(mLocalBufferData);
+}
+
+status_t TimestretchBufferProvider::getNextBuffer(
+ AudioBufferProvider::Buffer *pBuffer, int64_t pts)
+{
+ ALOGV("TimestretchBufferProvider(%p)::getNextBuffer(%p (%zu), %lld)",
+ this, pBuffer, pBuffer->frameCount, pts);
+
+ // BYPASS
+ //return mTrackBufferProvider->getNextBuffer(pBuffer, pts);
+
+ // check if previously processed data is sufficient.
+ if (pBuffer->frameCount <= mRemaining) {
+ ALOGV("previous sufficient");
+ pBuffer->raw = mLocalBufferData;
+ return OK;
+ }
+
+ // do we need to resize our buffer?
+ if (pBuffer->frameCount > mLocalBufferFrameCount) {
+ void *newmem;
+ if (posix_memalign(&newmem, 32, pBuffer->frameCount * mFrameSize) == OK) {
+ if (mRemaining != 0) {
+ memcpy(newmem, mLocalBufferData, mRemaining * mFrameSize);
+ }
+ free(mLocalBufferData);
+ mLocalBufferData = newmem;
+ mLocalBufferFrameCount = pBuffer->frameCount;
+ }
+ }
+
+ // need to fetch more data
+ const size_t outputDesired = pBuffer->frameCount - mRemaining;
+ mBuffer.frameCount = mSpeed == AUDIO_TIMESTRETCH_SPEED_NORMAL
+ ? outputDesired : outputDesired * mSpeed + 1;
+
+ status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer, pts);
+
+ ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
+ if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
+ ALOGD("buffer error");
+ if (mRemaining == 0) {
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+ return res;
+ } else { // return partial count
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = mRemaining;
+ return OK;
+ }
+ }
+
+ // time-stretch the data
+ size_t dstAvailable = min(mLocalBufferFrameCount - mRemaining, outputDesired);
+ size_t srcAvailable = mBuffer.frameCount;
+ processFrames((uint8_t*)mLocalBufferData + mRemaining * mFrameSize, &dstAvailable,
+ mBuffer.raw, &srcAvailable);
+
+ // release all data consumed
+ mBuffer.frameCount = srcAvailable;
+ mTrackBufferProvider->releaseBuffer(&mBuffer);
+
+ // update buffer vars with the actual data processed and return with buffer
+ mRemaining += dstAvailable;
+
+ pBuffer->raw = mLocalBufferData;
+ pBuffer->frameCount = mRemaining;
+
+ return OK;
+}
+
+void TimestretchBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
+{
+ ALOGV("TimestretchBufferProvider(%p)::releaseBuffer(%p (%zu))",
+ this, pBuffer, pBuffer->frameCount);
+
+ // BYPASS
+ //return mTrackBufferProvider->releaseBuffer(pBuffer);
+
+ // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
+ if (pBuffer->frameCount < mRemaining) {
+ memcpy(mLocalBufferData,
+ (uint8_t*)mLocalBufferData + pBuffer->frameCount * mFrameSize,
+ (mRemaining - pBuffer->frameCount) * mFrameSize);
+ mRemaining -= pBuffer->frameCount;
+ } else if (pBuffer->frameCount == mRemaining) {
+ mRemaining = 0;
+ } else {
+ LOG_ALWAYS_FATAL("Releasing more frames(%zu) than available(%zu)",
+ pBuffer->frameCount, mRemaining);
+ }
+
+ pBuffer->raw = NULL;
+ pBuffer->frameCount = 0;
+}
+
+void TimestretchBufferProvider::reset()
+{
+ mRemaining = 0;
+}
+
+status_t TimestretchBufferProvider::setPlaybackRate(float speed, float pitch)
+{
+ mSpeed = speed;
+ mPitch = pitch;
+ return OK;
+}
+
+void TimestretchBufferProvider::processFrames(void *dstBuffer, size_t *dstFrames,
+ const void *srcBuffer, size_t *srcFrames)
+{
+ ALOGV("processFrames(%zu %zu) remaining(%zu)", *dstFrames, *srcFrames, mRemaining);
+ // Note dstFrames is the required number of frames.
+
+ // Ensure consumption from src is as expected.
+ const size_t targetSrc = *dstFrames * mSpeed;
+ if (*srcFrames < targetSrc) { // limit dst frames to that possible
+ *dstFrames = *srcFrames / mSpeed;
+ } else if (*srcFrames > targetSrc + 1) {
+ *srcFrames = targetSrc + 1;
+ }
+
+ // Do the time stretch by memory copy without any local buffer.
+ if (*dstFrames <= *srcFrames) {
+ size_t copySize = mFrameSize * *dstFrames;
+ memcpy(dstBuffer, srcBuffer, copySize);
+ } else {
+ // cyclically repeat the source.
+ for (size_t count = 0; count < *dstFrames; count += *srcFrames) {
+ size_t remaining = min(*srcFrames, *dstFrames - count);
+ memcpy((uint8_t*)dstBuffer + mFrameSize * count,
+ srcBuffer, mFrameSize * *srcFrames);
+ }
+ }
+}
+
// ----------------------------------------------------------------------------
} // namespace android
diff --git a/services/audioflinger/BufferProviders.h b/services/audioflinger/BufferProviders.h
index 7145b80..2b6ea47 100644
--- a/services/audioflinger/BufferProviders.h
+++ b/services/audioflinger/BufferProviders.h
@@ -146,6 +146,45 @@
const audio_format_t mOutputFormat;
};
+// TimestretchBufferProvider derives from PassthruBufferProvider for time stretching
+class TimestretchBufferProvider : public PassthruBufferProvider {
+public:
+ TimestretchBufferProvider(int32_t channelCount,
+ audio_format_t format, uint32_t sampleRate, float speed, float pitch);
+ virtual ~TimestretchBufferProvider();
+
+ // Overrides AudioBufferProvider methods
+ virtual status_t getNextBuffer(Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(Buffer* buffer);
+
+ // Overrides PassthruBufferProvider
+ virtual void reset();
+
+ virtual status_t setPlaybackRate(float speed, float pitch);
+
+ // processes frames
+ // dstBuffer is where to place the data
+ // dstFrames [in/out] is the desired frames (return with actual placed in buffer)
+ // srcBuffer is the source data
+ // srcFrames [in/out] is the available source frames (return with consumed)
+ virtual void processFrames(void *dstBuffer, size_t *dstFrames,
+ const void *srcBuffer, size_t *srcFrames);
+
+protected:
+ const uint32_t mChannelCount;
+ const audio_format_t mFormat;
+ const uint32_t mSampleRate; // const for now (TODO change this)
+ const size_t mFrameSize;
+ float mSpeed;
+ float mPitch;
+
+private:
+ AudioBufferProvider::Buffer mBuffer;
+ size_t mLocalBufferFrameCount;
+ void *mLocalBufferData;
+ size_t mRemaining;
+};
+
// ----------------------------------------------------------------------------
} // namespace android
diff --git a/services/audioflinger/SpdifStreamOut.cpp b/services/audioflinger/SpdifStreamOut.cpp
index d23588e..45b541a 100644
--- a/services/audioflinger/SpdifStreamOut.cpp
+++ b/services/audioflinger/SpdifStreamOut.cpp
@@ -32,10 +32,12 @@
* If the AudioFlinger is processing encoded data and the HAL expects
* PCM then we need to wrap the data in an SPDIF wrapper.
*/
-SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags)
+SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev,
+ audio_output_flags_t flags,
+ audio_format_t format)
: AudioStreamOut(dev,flags)
, mRateMultiplier(1)
- , mSpdifEncoder(this)
+ , mSpdifEncoder(this, format)
, mRenderPositionHal(0)
, mPreviousHalPosition32(0)
{
@@ -49,15 +51,15 @@
{
struct audio_config customConfig = *config;
- customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
- customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
-
// Some data bursts run at a higher sample rate.
+ // TODO Move this into the audio_utils as a static method.
switch(config->format) {
case AUDIO_FORMAT_E_AC3:
mRateMultiplier = 4;
break;
case AUDIO_FORMAT_AC3:
+ case AUDIO_FORMAT_DTS:
+ case AUDIO_FORMAT_DTS_HD:
mRateMultiplier = 1;
break;
default:
@@ -67,6 +69,9 @@
}
customConfig.sample_rate = config->sample_rate * mRateMultiplier;
+ customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+ customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
// Always print this because otherwise it could be very confusing if the
// HAL and AudioFlinger are using different formats.
// Print before open() because HAL may modify customConfig.
diff --git a/services/audioflinger/SpdifStreamOut.h b/services/audioflinger/SpdifStreamOut.h
index cb82ac7..d81c064 100644
--- a/services/audioflinger/SpdifStreamOut.h
+++ b/services/audioflinger/SpdifStreamOut.h
@@ -38,7 +38,8 @@
class SpdifStreamOut : public AudioStreamOut {
public:
- SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags);
+ SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags,
+ audio_format_t format);
virtual ~SpdifStreamOut() { }
@@ -77,8 +78,9 @@
class MySPDIFEncoder : public SPDIFEncoder
{
public:
- MySPDIFEncoder(SpdifStreamOut *spdifStreamOut)
- : mSpdifStreamOut(spdifStreamOut)
+ MySPDIFEncoder(SpdifStreamOut *spdifStreamOut, audio_format_t format)
+ : SPDIFEncoder(format)
+ , mSpdifStreamOut(spdifStreamOut)
{
}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 1a20fae..b30fd20 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1608,13 +1608,19 @@
// If you change this calculation, also review the start threshold which is related.
if (!(*flags & IAudioFlinger::TRACK_FAST)
&& audio_is_linear_pcm(format) && sharedBuffer == 0) {
+ // this must match AudioTrack.cpp calculateMinFrameCount().
+ // TODO: Move to a common library
uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
if (minBufCount < 2) {
minBufCount = 2;
}
+ // For normal mixing tracks, if speed is > 1.0f (normal), AudioTrack
+ // or the client should compute and pass in a larger buffer request.
size_t minFrameCount =
- minBufCount * sourceFramesNeeded(sampleRate, mNormalFrameCount, mSampleRate);
+ minBufCount * sourceFramesNeededWithTimestretch(
+ sampleRate, mNormalFrameCount,
+ mSampleRate, AUDIO_TIMESTRETCH_SPEED_NORMAL /*speed*/);
if (frameCount < minFrameCount) { // including frameCount == 0
frameCount = minFrameCount;
}
@@ -3592,21 +3598,17 @@
// hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
// during last round
size_t desiredFrames;
- uint32_t sr = track->sampleRate();
- if (sr == mSampleRate) {
- desiredFrames = mNormalFrameCount;
- } else {
- desiredFrames = sourceFramesNeeded(sr, mNormalFrameCount, mSampleRate);
- // add frames already consumed but not yet released by the resampler
- // because mAudioTrackServerProxy->framesReady() will include these frames
- desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
-#if 0
- // the minimum track buffer size is normally twice the number of frames necessary
- // to fill one buffer and the resampler should not leave more than one buffer worth
- // of unreleased frames after each pass, but just in case...
- ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
-#endif
- }
+ const uint32_t sampleRate = track->mAudioTrackServerProxy->getSampleRate();
+ float speed, pitch;
+ track->mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
+
+ desiredFrames = sourceFramesNeededWithTimestretch(
+ sampleRate, mNormalFrameCount, mSampleRate, speed);
+ // TODO: ONLY USED FOR LEGACY RESAMPLERS, remove when they are removed.
+ // add frames already consumed but not yet released by the resampler
+ // because mAudioTrackServerProxy->framesReady() will include these frames
+ desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+
uint32_t minFrames = 1;
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
@@ -3769,6 +3771,17 @@
AudioMixer::RESAMPLE,
AudioMixer::SAMPLE_RATE,
(void *)(uintptr_t)reqSampleRate);
+
+ // set the playback rate as an float array {speed, pitch}
+ float playbackRate[2];
+ track->mAudioTrackServerProxy->getPlaybackRate(
+ &playbackRate[0] /*speed*/, &playbackRate[1] /*pitch*/);
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TIMESTRETCH,
+ AudioMixer::PLAYBACK_RATE,
+ playbackRate);
+
/*
* Select the appropriate output buffer for the track.
*
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 1566b1f..da2d634 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -903,9 +903,14 @@
mPreviousTimestampValid = false;
return INVALID_OPERATION;
}
+ // FIXME Not accurate under dynamic changes of sample rate and speed.
+ // Do not use track's mSampleRate as it is not current for mixer tracks.
+ uint32_t sampleRate = mAudioTrackServerProxy->getSampleRate();
+ float speed, pitch;
+ mAudioTrackServerProxy->getPlaybackRate(&speed, &pitch);
uint32_t unpresentedFrames =
- ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
- playbackThread->mSampleRate;
+ ((double) playbackThread->mLatchQ.mUnpresentedFrames * sampleRate * speed)
+ / playbackThread->mSampleRate;
// FIXME Since we're using a raw pointer as the key, it is theoretically possible
// for a brand new track to share the same address as a recently destroyed
// track, and thus for us to get the frames released of the wrong track.
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e9c96c6..dabfafa 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -122,7 +122,7 @@
// should be ok for now.
static CameraService *gCameraService;
-CameraService::CameraService() : mEventLog(DEFAULT_EVICTION_LOG_LENGTH),
+CameraService::CameraService() : mEventLog(DEFAULT_EVENT_LOG_LENGTH),
mLastUserId(DEFAULT_LAST_USER_ID), mSoundRef(0), mModule(0), mFlashlight(0) {
ALOGI("CameraService started (pid=%d)", getpid());
gCameraService = this;
@@ -242,6 +242,8 @@
}
if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
+ logDeviceRemoved(id, String8::format("Device status changed from %d to %d", oldStatus,
+ newStatus));
sp<BasicClient> clientToDisconnect;
{
// Don't do this in updateStatus to avoid deadlock over mServiceLock
@@ -274,6 +276,10 @@
}
} else {
+ if (oldStatus == ICameraServiceListener::Status::STATUS_NOT_PRESENT) {
+ logDeviceAdded(id, String8::format("Device status changed from %d to %d", oldStatus,
+ newStatus));
+ }
updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id);
}
@@ -765,8 +771,8 @@
} else {
// We only trust our own process to forward client UIDs
if (callingPid != getpid()) {
- ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",
- callingPid);
+ ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid %d)",
+ callingPid, clientUid);
return PERMISSION_DENIED;
}
}
@@ -796,10 +802,12 @@
return -EACCES;
}
- // Only allow clients who are being used by the current foreground device user.
- if (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID) {
- ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from non-foreground "
- "device user)", callingPid);
+ // Only allow clients who are being used by the current foreground device user, unless calling
+ // from our own process.
+ if (callingPid != getpid() &&
+ (mLastUserId != clientUserId && mLastUserId != DEFAULT_LAST_USER_ID)) {
+ ALOGE("CameraService::connect X (PID %d) rejected (cannot connect from previous "
+ "device user %d, current device user %d)", callingPid, clientUserId, mLastUserId);
return PERMISSION_DENIED;
}
@@ -934,7 +942,7 @@
mActiveClientManager.getIncompatibleClients(clientDescriptor);
String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
- "(PID %d, priority %d)", curTime.string(),
+ "(PID %d, priority %d) due to eviction policy", curTime.string(),
cameraId.string(), packageName.string(), clientPid,
getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
@@ -946,6 +954,7 @@
}
// Log the client's attempt
+ Mutex::Autolock l(mLogLock);
mEventLog.add(msg);
return -EBUSY;
@@ -967,12 +976,10 @@
i->getKey().string());
evictedClients.push_back(clientSp);
- String8 curTime = getFormattedCurrentTime();
-
// Log the clients evicted
- mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
- PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for "
- "package %s (PID %d, priority %" PRId32 ")", curTime.string(),
+ logEvent(String8::format("EVICT device %s client held by package %s (PID"
+ " %" PRId32 ", priority %" PRId32 ")\n - Evicted by device %s client for"
+ " package %s (PID %d, priority %" PRId32 ")",
i->getKey().string(), String8{clientSp->getPackageName()}.string(),
i->getOwnerId(), i->getPriority(), cameraId.string(),
packageName.string(), clientPid,
@@ -1027,6 +1034,8 @@
clientPackageName, clientUid, API_1, false, false, /*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1042,6 +1051,7 @@
/*out*/
sp<ICamera>& device) {
+ String8 id = String8::format("%d", cameraId);
int apiVersion = mModule->getModuleApiVersion();
if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
apiVersion < CAMERA_MODULE_API_VERSION_2_3) {
@@ -1053,16 +1063,19 @@
*/
ALOGE("%s: camera HAL module version %x doesn't support connecting to legacy HAL devices!",
__FUNCTION__, apiVersion);
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8("HAL module version doesn't support legacy HAL connections"));
return INVALID_OPERATION;
}
status_t ret = NO_ERROR;
- String8 id = String8::format("%d", cameraId);
sp<Client> client = nullptr;
ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName,
clientUid, API_1, true, false, /*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1086,6 +1099,8 @@
/*out*/client);
if(ret != NO_ERROR) {
+ logRejected(id, getCallingPid(), String8(clientPackageName),
+ String8::format("%s (%d)", strerror(-ret), ret));
return ret;
}
@@ -1426,6 +1441,8 @@
newUserId = DEFAULT_LAST_USER_ID;
}
+ logUserSwitch(mLastUserId, newUserId);
+
mLastUserId = newUserId;
// Current user has switched, evict all current clients.
@@ -1444,12 +1461,12 @@
ALOGE("Evicting conflicting client for camera ID %s due to user change",
i->getKey().string());
+
// Log the clients evicted
- mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
+ logEvent(String8::format("EVICT device %s client held by package %s (PID %"
PRId32 ", priority %" PRId32 ")\n - Evicted due to user switch.",
- curTime.string(), i->getKey().string(),
- String8{clientSp->getPackageName()}.string(), i->getOwnerId(),
- i->getPriority()));
+ i->getKey().string(), String8{clientSp->getPackageName()}.string(),
+ i->getOwnerId(), i->getPriority()));
}
@@ -1470,22 +1487,52 @@
mServiceLock.lock();
}
-void CameraService::logDisconnected(const String8& cameraId, int clientPid,
- const String8& clientPackage) {
-
+void CameraService::logEvent(const char* event) {
String8 curTime = getFormattedCurrentTime();
- // Log the clients evicted
- mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)",
- curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+ Mutex::Autolock l(mLogLock);
+ mEventLog.add(String8::format("%s : %s", curTime.string(), event));
}
-void CameraService::logConnected(const String8& cameraId, int clientPid,
- const String8& clientPackage) {
-
- String8 curTime = getFormattedCurrentTime();
+void CameraService::logDisconnected(const char* cameraId, int clientPid,
+ const char* clientPackage) {
// Log the clients evicted
- mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)",
- curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
+ logEvent(String8::format("DISCONNECT device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
+
+void CameraService::logConnected(const char* cameraId, int clientPid,
+ const char* clientPackage) {
+ // Log the clients evicted
+ logEvent(String8::format("CONNECT device %s client for package %s (PID %d)", cameraId,
+ clientPackage, clientPid));
+}
+
+void CameraService::logRejected(const char* cameraId, int clientPid,
+ const char* clientPackage, const char* reason) {
+ // Log the client rejected
+ logEvent(String8::format("REJECT device %s client for package %s (PID %d), reason: (%s)",
+ cameraId, clientPackage, clientPid, reason));
+}
+
+void CameraService::logUserSwitch(int oldUserId, int newUserId) {
+ // Log the new and old users
+ logEvent(String8::format("USER_SWITCH from old user: %d , to new user: %d", oldUserId,
+ newUserId));
+}
+
+void CameraService::logDeviceRemoved(const char* cameraId, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("REMOVE device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logDeviceAdded(const char* cameraId, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("ADD device %s, reason: (%s)", cameraId, reason));
+}
+
+void CameraService::logClientDied(int clientPid, const char* reason) {
+ // Log the device removal
+ logEvent(String8::format("DIED client(s) with PID %d, reason: (%s)", clientPid, reason));
}
status_t CameraService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
@@ -1911,7 +1958,7 @@
}
status_t CameraService::dump(int fd, const Vector<String16>& args) {
- String8 result;
+ String8 result("Dump of the Camera Service:\n");
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
result.appendFormat("Permission Denial: "
"can't dump CameraService from pid=%d, uid=%d\n",
@@ -1957,12 +2004,15 @@
result = String8("Prior client events (most recent at top):\n");
- for (const auto& msg : mEventLog) {
- result.appendFormat("%s\n", msg.string());
- }
+ {
+ Mutex::Autolock l(mLogLock);
+ for (const auto& msg : mEventLog) {
+ result.appendFormat("%s\n", msg.string());
+ }
- if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) {
- result.append("...\n");
+ if (mEventLog.size() == DEFAULT_EVENT_LOG_LENGTH) {
+ result.append("...\n");
+ }
}
write(fd, result.string(), result.size());
@@ -2094,10 +2144,12 @@
/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
/**
- * While tempting to promote the wp<IBinder> into a sp,
- * it's actually not supported by the binder driver
+ * While tempting to promote the wp<IBinder> into a sp, it's actually not supported by the
+ * binder driver
*/
+ logClientDied(getCallingPid(), String8("Binder died unexpectedly"));
+
// check torch client
handleTorchClientBinderDied(who);
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index ca1c504..9eda205 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -91,7 +91,7 @@
static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
// Default number of messages to store in eviction log
- static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;
+ static const size_t DEFAULT_EVENT_LOG_LENGTH = 100;
enum {
// Default last user id
@@ -492,6 +492,7 @@
// Circular buffer for storing event logging for dumps
RingBuffer<String8> mEventLog;
+ Mutex mLogLock;
// UID of last user.
int mLastUserId;
@@ -546,14 +547,45 @@
void doUserSwitch(int newUserId);
/**
- * Add a event log message that a client has been disconnected.
+ * Add an event log message.
*/
- void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+ void logEvent(const char* event);
/**
- * Add a event log message that a client has been connected.
+ * Add an event log message that a client has been disconnected.
*/
- void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+ void logDisconnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that a client has been connected.
+ */
+ void logConnected(const char* cameraId, int clientPid, const char* clientPackage);
+
+ /**
+ * Add an event log message that a client's connect attempt has been rejected.
+ */
+ void logRejected(const char* cameraId, int clientPid, const char* clientPackage,
+ const char* reason);
+
+ /**
+ * Add an event log message that the current device user has been switched.
+ */
+ void logUserSwitch(int oldUserId, int newUserId);
+
+ /**
+ * Add an event log message that a device has been removed by the HAL
+ */
+ void logDeviceRemoved(const char* cameraId, const char* reason);
+
+ /**
+ * Add an event log message that a device has been added by the HAL
+ */
+ void logDeviceAdded(const char* cameraId, const char* reason);
+
+ /**
+ * Add an event log message that a client has unexpectedly died.
+ */
+ void logClientDied(int clientPid, const char* reason);
int mNumberOfCameras;
@@ -714,9 +746,10 @@
String8 clientName8(clientPackageName);
int clientPid = getCallingPid();
- ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and "
+ ALOGI("CameraService::connect call (PID %d \"%s\", camera ID %s) for HAL version %s and "
"Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
- halVersion, static_cast<int>(effectiveApiLevel));
+ (halVersion == -1) ? "default" : std::to_string(halVersion).c_str(),
+ static_cast<int>(effectiveApiLevel));
sp<CLIENT> client = nullptr;
{
@@ -734,7 +767,15 @@
if((ret = validateConnectLocked(cameraId, /*inout*/clientUid)) != NO_ERROR) {
return ret;
}
- mLastUserId = multiuser_get_user_id(clientUid);
+ int userId = multiuser_get_user_id(clientUid);
+
+ if (userId != mLastUserId && clientPid != getpid() ) {
+ // If no previous user ID had been set, set to the user of the caller.
+ logUserSwitch(mLastUserId, userId);
+ LOG_ALWAYS_FATAL_IF(mLastUserId != DEFAULT_LAST_USER_ID,
+ "Invalid state: Should never update user ID here unless was default");
+ mLastUserId = userId;
+ }
// Check the shim parameters after acquiring lock, if they have already been updated and
// we were doing a shim update, return immediately