summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoFactory.cpp16
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp16
-rw-r--r--drm/mediadrm/plugins/clearkey/CryptoPlugin.h11
-rw-r--r--drm/mediadrm/plugins/clearkey/DrmPlugin.cpp12
-rw-r--r--drm/mediadrm/plugins/clearkey/SessionLibrary.cpp7
-rw-r--r--drm/mediadrm/plugins/clearkey/SessionLibrary.h4
-rw-r--r--include/media/IOMX.h19
-rw-r--r--include/media/stagefright/ACodec.h14
-rw-r--r--media/libmedia/IOMX.cpp32
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp27
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp71
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h1
-rw-r--r--media/libstagefright/ACodec.cpp169
-rw-r--r--media/libstagefright/OMXClient.cpp12
-rw-r--r--media/libstagefright/Utils.cpp6
-rwxr-xr-x[-rw-r--r--]media/libstagefright/codecs/avcdec/SoftAVCDec.cpp51
-rwxr-xr-x[-rw-r--r--]media/libstagefright/codecs/avcdec/SoftAVCDec.h1
-rw-r--r--media/libstagefright/codecs/opus/dec/SoftOpus.cpp8
-rw-r--r--media/libstagefright/include/OMX.h8
-rw-r--r--media/libstagefright/include/OMXNodeInstance.h17
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.cpp54
-rw-r--r--media/libstagefright/omx/GraphicBufferSource.h3
-rw-r--r--media/libstagefright/omx/OMX.cpp15
-rw-r--r--media/libstagefright/omx/OMXNodeInstance.cpp109
-rw-r--r--media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp12
-rw-r--r--services/audioflinger/ServiceUtilities.cpp5
-rw-r--r--services/audioflinger/Threads.cpp38
-rw-r--r--services/audioflinger/Threads.h3
28 files changed, 579 insertions, 162 deletions
diff --git a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
index ee3189b..eeb64c3 100644
--- a/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
+++ b/drm/mediadrm/plugins/clearkey/CryptoFactory.cpp
@@ -43,10 +43,18 @@ android::status_t CryptoFactory::createPlugin(
return android::BAD_VALUE;
}
- android::sp<Session> session = SessionLibrary::get()->findSession(
- data, size);
- *plugin = new CryptoPlugin(session);
- return android::OK;
+ android::Vector<uint8_t> sessionId;
+ sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size);
+
+ CryptoPlugin *clearKeyPlugin = new CryptoPlugin(sessionId);
+ android::status_t result = clearKeyPlugin->getInitStatus();
+ if (result == android::OK) {
+ *plugin = clearKeyPlugin;
+ } else {
+ delete clearKeyPlugin;
+ *plugin = NULL;
+ }
+ return result;
}
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
index adad136..53cbf80 100644
--- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.cpp
@@ -19,9 +19,9 @@
#include <utils/Log.h>
#include <media/stagefright/MediaErrors.h>
-#include <utils/Errors.h>
#include "CryptoPlugin.h"
+#include "SessionLibrary.h"
namespace clearkeydrm {
@@ -80,4 +80,18 @@ ssize_t CryptoPlugin::decrypt(bool secure, const KeyId keyId, const Iv iv,
}
}
+android::status_t CryptoPlugin::setMediaDrmSession(
+ const android::Vector<uint8_t>& sessionId) {
+ if (!sessionId.size()) {
+ mSession.clear();
+ } else {
+ mSession = SessionLibrary::get()->findSession(sessionId);
+ if (!mSession.get()) {
+ return android::ERROR_DRM_SESSION_NOT_OPENED;
+ }
+ }
+ return android::OK;
+}
+
+
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
index 002d9e0..fd38f28 100644
--- a/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/CryptoPlugin.h
@@ -31,7 +31,10 @@ namespace clearkeydrm {
class CryptoPlugin : public android::CryptoPlugin {
public:
- CryptoPlugin(const android::sp<Session>& session) : mSession(session) {}
+ CryptoPlugin(const android::Vector<uint8_t>& sessionId) {
+ mInitStatus = setMediaDrmSession(sessionId);
+ }
+
virtual ~CryptoPlugin() {}
virtual bool requiresSecureDecoderComponent(const char* mime) const {
@@ -45,10 +48,16 @@ public:
const SubSample* subSamples, size_t numSubSamples,
void* dstPtr, android::AString* errorDetailMsg);
+ virtual android::status_t setMediaDrmSession(
+ const android::Vector<uint8_t>& sessionId);
+
+ android::status_t getInitStatus() const {return mInitStatus;}
+
private:
DISALLOW_EVIL_CONSTRUCTORS(CryptoPlugin);
android::sp<Session> mSession;
+ android::status_t mInitStatus;
};
} // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
index 6b8c772..e5ee403 100644
--- a/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/DrmPlugin.cpp
@@ -37,7 +37,9 @@ status_t DrmPlugin::openSession(Vector<uint8_t>& sessionId) {
status_t DrmPlugin::closeSession(const Vector<uint8_t>& sessionId) {
sp<Session> session = mSessionLibrary->findSession(sessionId);
- mSessionLibrary->destroySession(session);
+ if (session.get()) {
+ mSessionLibrary->destroySession(session);
+ }
return android::OK;
}
@@ -55,8 +57,11 @@ status_t DrmPlugin::getKeyRequest(
return android::ERROR_DRM_CANNOT_HANDLE;
}
*keyRequestType = DrmPlugin::kKeyRequestType_Initial;
- sp<Session> session = mSessionLibrary->findSession(scope);
defaultUrl.clear();
+ sp<Session> session = mSessionLibrary->findSession(scope);
+ if (!session.get()) {
+ return android::ERROR_DRM_SESSION_NOT_OPENED;
+ }
return session->getKeyRequest(initData, initDataType, &request);
}
@@ -65,6 +70,9 @@ status_t DrmPlugin::provideKeyResponse(
const Vector<uint8_t>& response,
Vector<uint8_t>& keySetId) {
sp<Session> session = mSessionLibrary->findSession(scope);
+ if (!session.get()) {
+ return android::ERROR_DRM_SESSION_NOT_OPENED;
+ }
status_t res = session->provideKeyResponse(response);
if (res == android::OK) {
keySetId.clear();
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
index d047c53..46d7f77 100644
--- a/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.cpp
@@ -63,13 +63,6 @@ const sp<Session>& SessionLibrary::findSession(
return mSessions.valueFor(sessionId);
}
-const sp<Session>& SessionLibrary::findSession(
- const void* data, size_t size) {
- Vector<uint8_t> sessionId;
- sessionId.appendArray(reinterpret_cast<const uint8_t*>(data), size);
- return findSession(sessionId);
-}
-
void SessionLibrary::destroySession(const sp<Session>& session) {
Mutex::Autolock lock(mSessionsLock);\
mSessions.removeItem(session->sessionId());
diff --git a/drm/mediadrm/plugins/clearkey/SessionLibrary.h b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
index 56c8828..199ad64 100644
--- a/drm/mediadrm/plugins/clearkey/SessionLibrary.h
+++ b/drm/mediadrm/plugins/clearkey/SessionLibrary.h
@@ -36,8 +36,6 @@ public:
const android::sp<Session>& findSession(
const android::Vector<uint8_t>& sessionId);
- const android::sp<Session>& findSession(const void* data, size_t size);
-
void destroySession(const android::sp<Session>& session);
private:
@@ -50,7 +48,7 @@ private:
android::Mutex mSessionsLock;
uint32_t mNextSessionId;
- android::KeyedVector<android::Vector<uint8_t>, android::sp<Session> >
+ android::DefaultKeyedVector<android::Vector<uint8_t>, android::sp<Session> >
mSessions;
};
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
index 26cc73e..84fdf83 100644
--- a/include/media/IOMX.h
+++ b/include/media/IOMX.h
@@ -92,7 +92,7 @@ public:
node_id node, OMX_U32 portIndex, OMX_BOOL enable,
OMX_U32 maxFrameWidth, OMX_U32 maxFrameHeight) = 0;
- virtual status_t configureVideoTunnelMode(
+ virtual status_t configureVideoTunnelMode(
node_id node, OMX_U32 portIndex, OMX_BOOL tunneled,
OMX_U32 audioHwSync, native_handle_t **sidebandHandle) = 0;
@@ -152,13 +152,23 @@ public:
virtual status_t freeBuffer(
node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
- virtual status_t fillBuffer(node_id node, buffer_id buffer) = 0;
-
+ enum {
+ kFenceTimeoutMs = 1000
+ };
+ // Calls OMX_FillBuffer on buffer, and passes |fenceFd| to component if it supports
+ // fences. Otherwise, it waits on |fenceFd| before calling OMX_FillBuffer.
+ // Takes ownership of |fenceFd| even if this call fails.
+ virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd = -1) = 0;
+
+ // Calls OMX_EmptyBuffer on buffer (after updating buffer header with |range_offset|,
+ // |range_length|, |flags| and |timestamp|). Passes |fenceFd| to component if it
+ // supports fences. Otherwise, it waits on |fenceFd| before calling OMX_EmptyBuffer.
+ // Takes ownership of |fenceFd| even if this call fails.
virtual status_t emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp) = 0;
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd = -1) = 0;
virtual status_t getExtensionIndex(
node_id node,
@@ -190,6 +200,7 @@ struct omx_message {
} type;
IOMX::node_id node;
+ int fenceFd; // used for EMPTY_BUFFER_DONE and FILL_BUFFER_DONE; client must close this
union {
// if type == EVENT
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index bbecc80..f7a3df7 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -160,11 +160,25 @@ private:
sp<ABuffer> mData;
sp<GraphicBuffer> mGraphicBuffer;
+ int mFenceFd;
+
+ // The following field and 4 methods are used for debugging only
+ bool mIsReadFence;
+ // Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored.
+ void setReadFence(int fenceFd, const char *dbg);
+ void setWriteFence(int fenceFd, const char *dbg);
+ // Log error, if the current fence is not a read/write fence.
+ void checkReadFence(const char *dbg);
+ void checkWriteFence(const char *dbg);
};
static const char *_asString(BufferInfo::Status s);
void dumpBuffers(OMX_U32 portIndex);
+ // If |fd| is non-negative, waits for fence with |fd| and logs an error if it fails. Returns
+ // the error code or OK on success. If |fd| is negative, it returns OK
+ status_t waitForFence(int fd, const char *dbg);
+
#if TRACK_BUFFER_TIMING
struct BufferStats {
int64_t mEmptyBufferTimeUs;
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index cac2f7f..ca1cdc7 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -511,11 +511,15 @@ public:
return reply.readInt32();
}
- virtual status_t fillBuffer(node_id node, buffer_id buffer) {
+ virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeInt32((int32_t)node);
data.writeInt32((int32_t)buffer);
+ data.writeInt32(fenceFd >= 0);
+ if (fenceFd >= 0) {
+ data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
+ }
remote()->transact(FILL_BUFFER, data, &reply);
return reply.readInt32();
@@ -525,7 +529,7 @@ public:
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
Parcel data, reply;
data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
data.writeInt32((int32_t)node);
@@ -534,6 +538,10 @@ public:
data.writeInt32(range_length);
data.writeInt32(flags);
data.writeInt64(timestamp);
+ data.writeInt32(fenceFd >= 0);
+ if (fenceFd >= 0) {
+ data.writeFileDescriptor(fenceFd, true /* takeOwnership */);
+ }
remote()->transact(EMPTY_BUFFER, data, &reply);
return reply.readInt32();
@@ -1012,7 +1020,9 @@ status_t BnOMX::onTransact(
node_id node = (node_id)data.readInt32();
buffer_id buffer = (buffer_id)data.readInt32();
- reply->writeInt32(fillBuffer(node, buffer));
+ bool haveFence = data.readInt32();
+ int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
+ reply->writeInt32(fillBuffer(node, buffer, fenceFd));
return NO_ERROR;
}
@@ -1027,11 +1037,10 @@ status_t BnOMX::onTransact(
OMX_U32 range_length = data.readInt32();
OMX_U32 flags = data.readInt32();
OMX_TICKS timestamp = data.readInt64();
-
- reply->writeInt32(
- emptyBuffer(
- node, buffer, range_offset, range_length,
- flags, timestamp));
+ bool haveFence = data.readInt32();
+ int fenceFd = haveFence ? ::dup(data.readFileDescriptor()) : -1;
+ reply->writeInt32(emptyBuffer(
+ node, buffer, range_offset, range_length, flags, timestamp, fenceFd));
return NO_ERROR;
}
@@ -1072,7 +1081,9 @@ public:
Parcel data, reply;
data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
data.write(&msg, sizeof(msg));
-
+ if (msg.fenceFd >= 0) {
+ data.writeFileDescriptor(msg.fenceFd, true /* takeOwnership */);
+ }
ALOGV("onMessage writing message %d, size %zu", msg.type, sizeof(msg));
remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
@@ -1090,6 +1101,9 @@ status_t BnOMXObserver::onTransact(
omx_message msg;
data.read(&msg, sizeof(msg));
+ if (msg.fenceFd >= 0) {
+ msg.fenceFd = ::dup(data.readFileDescriptor());
+ }
ALOGV("onTransact reading message %d, size %zu", msg.type, sizeof(msg));
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 2ba05c0..02c1c38 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1853,20 +1853,23 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(
me, buffer->raw, buffer->size, me->mCallbackCookie,
CB_EVENT_FILL_BUFFER);
- if ((me->mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0 &&
- actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) {
- // We've reached EOS but the audio track is not stopped yet,
- // keep playing silence.
+ // Log when no data is returned from the callback.
+ // (1) We may have no data (especially with network streaming sources).
+ // (2) We may have reached the EOS and the audio track is not stopped yet.
+ // Note that AwesomePlayer/AudioPlayer will only return zero size when it reaches the EOS.
+ // NuPlayerRenderer will return zero when it doesn't have data (it doesn't block to fill).
+ //
+ // This is a benign busy-wait, with the next data request generated 10 ms or more later;
+ // nevertheless for power reasons, we don't want to see too many of these.
- memset(buffer->raw, 0, buffer->size);
- actualSize = buffer->size;
- }
+ ALOGV_IF(actualSize == 0 && buffer->size > 0, "callbackwrapper: empty buffer returned");
+ me->mBytesWritten += actualSize; // benign race with reader.
buffer->size = actualSize;
} break;
-
case AudioTrack::EVENT_STREAM_END:
+ // currently only occurs for offloaded callbacks
ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
(*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
me->mCallbackCookie, CB_EVENT_STREAM_END);
@@ -1879,11 +1882,15 @@ void MediaPlayerService::AudioOutput::CallbackWrapper(
break;
case AudioTrack::EVENT_UNDERRUN:
- // This occurs when there is no data available, typically occurring
+ // This occurs when there is no data available, typically
// when there is a failure to supply data to the AudioTrack. It can also
// occur in non-offloaded mode when the audio device comes out of standby.
//
- // If you see this at the start of playback, there probably was a glitch.
+ // If an AudioTrack underruns it outputs silence. Since this happens suddenly
+ // it may sound like an audible pop or glitch.
+ //
+ // The underrun event is sent once per track underrun; the condition is reset
+ // when more data is sent to the AudioTrack.
ALOGI("callbackwrapper: EVENT_UNDERRUN (discarded)");
break;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 409dedf..89c261c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -19,7 +19,7 @@
#include <utils/Log.h>
#include "NuPlayerRenderer.h"
-
+#include <cutils/properties.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
@@ -36,6 +36,25 @@
namespace android {
+/*
+ * Example of common configuration settings in shell script form
+
+ #Turn offload audio off (use PCM for Play Music) -- AudioPolicyManager
+ adb shell setprop audio.offload.disable 1
+
+ #Allow offload audio with video (requires offloading to be enabled) -- AudioPolicyManager
+ adb shell setprop audio.offload.video 1
+
+ #Use audio callbacks for PCM data
+ adb shell setprop media.stagefright.audio.cbk 1
+
+ * These configurations take effect for the next track played (not the current track).
+ */
+
+static inline bool getUseAudioCallbackSetting() {
+ return property_get_bool("media.stagefright.audio.cbk", false /* default_value */);
+}
+
// Maximum time in paused state when offloading audio decompression. When elapsed, the AudioSink
// is closed to allow the audio DSP to power down.
static const int64_t kOffloadPauseMaxUs = 10000000ll;
@@ -87,6 +106,7 @@ NuPlayer::Renderer::Renderer(
mCurrentPcmInfo(AUDIO_PCMINFO_INITIALIZER),
mTotalBuffersQueued(0),
mLastAudioBufferDrained(0),
+ mUseAudioCallback(false),
mWakeLock(new AWakeLock()) {
mMediaClock = new MediaClock;
mPlaybackRate = mPlaybackSettings.mSpeed;
@@ -593,7 +613,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
if (mDrainAudioQueuePending || mSyncQueues || mPaused
- || offloadingAudio()) {
+ || mUseAudioCallback) {
return;
}
@@ -642,12 +662,14 @@ size_t NuPlayer::Renderer::AudioSinkCallback(
case MediaPlayerBase::AudioSink::CB_EVENT_STREAM_END:
{
+ ALOGV("AudioSink::CB_EVENT_STREAM_END");
me->notifyEOS(true /* audio */, ERROR_END_OF_STREAM);
break;
}
case MediaPlayerBase::AudioSink::CB_EVENT_TEAR_DOWN:
{
+ ALOGV("AudioSink::CB_EVENT_TEAR_DOWN");
me->notifyAudioTearDown();
break;
}
@@ -659,7 +681,7 @@ size_t NuPlayer::Renderer::AudioSinkCallback(
size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
Mutex::Autolock autoLock(mLock);
- if (!offloadingAudio() || mPaused) {
+ if (!mUseAudioCallback || mPaused) {
return 0;
}
@@ -667,13 +689,13 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
size_t sizeCopied = 0;
bool firstEntry = true;
+ QueueEntry *entry; // will be valid after while loop if hasEOS is set.
while (sizeCopied < size && !mAudioQueue.empty()) {
- QueueEntry *entry = &*mAudioQueue.begin();
+ entry = &*mAudioQueue.begin();
if (entry->mBuffer == NULL) { // EOS
hasEOS = true;
mAudioQueue.erase(mAudioQueue.begin());
- entry = NULL;
break;
}
@@ -681,7 +703,7 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
firstEntry = false;
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+ ALOGV("fillAudioBuffer: rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
setAudioFirstAnchorTimeIfNeeded_l(mediaTimeUs);
}
@@ -714,10 +736,28 @@ size_t NuPlayer::Renderer::fillAudioBuffer(void *buffer, size_t size) {
mMediaClock->updateAnchor(nowMediaUs, nowUs, INT64_MAX);
}
+ // for non-offloaded audio, we need to compute the frames written because
+ // there is no EVENT_STREAM_END notification. The frames written gives
+ // an estimate on the pending played out duration.
+ if (!offloadingAudio()) {
+ mNumFramesWritten += sizeCopied / mAudioSink->frameSize();
+ }
+
if (hasEOS) {
(new AMessage(kWhatStopAudioSink, this))->post();
+ // As there is currently no EVENT_STREAM_END callback notification for
+ // non-offloaded audio tracks, we need to post the EOS ourselves.
+ if (!offloadingAudio()) {
+ int64_t postEOSDelayUs = 0;
+ if (mAudioSink->needsTrailingPadding()) {
+ postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs());
+ }
+ ALOGV("fillAudioBuffer: notifyEOS "
+ "mNumFramesWritten:%u finalResult:%d postEOSDelay:%lld",
+ mNumFramesWritten, entry->mFinalResult, (long long)postEOSDelayUs);
+ notifyEOS(true /* audio */, entry->mFinalResult, postEOSDelayUs);
+ }
}
-
return sizeCopied;
}
@@ -778,7 +818,8 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
if (entry->mOffset == 0 && entry->mBuffer->size() > 0) {
int64_t mediaTimeUs;
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
- ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
+ ALOGV("onDrainAudioQueue: rendering audio at media time %.2f secs",
+ mediaTimeUs / 1E6);
onNewAudioMediaTime(mediaTimeUs);
}
@@ -1230,9 +1271,8 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
++mAudioDrainGeneration;
prepareForMediaRenderingStart_l();
- if (offloadingAudio()) {
- clearAudioFirstAnchorTime_l();
- }
+ // the frame count will be reset after flush.
+ clearAudioFirstAnchorTime_l();
}
mDrainAudioQueuePending = false;
@@ -1590,6 +1630,7 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
offloadFlags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
audioSinkChanged = true;
mAudioSink->close();
+
err = mAudioSink->open(
sampleRate,
numChannels,
@@ -1623,6 +1664,7 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER;
ALOGV("openAudioSink: offload failed");
}
+ mUseAudioCallback = true; // offload mode transfers data through callback
}
}
if (!offloadOnly && !offloadingAudio()) {
@@ -1646,14 +1688,17 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
audioSinkChanged = true;
mAudioSink->close();
mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER;
+ // Note: It is possible to set up the callback, but not use it to send audio data.
+ // This requires a fix in AudioSink to explicitly specify the transfer mode.
+ mUseAudioCallback = getUseAudioCallbackSetting();
status_t err = mAudioSink->open(
sampleRate,
numChannels,
(audio_channel_mask_t)channelMask,
AUDIO_FORMAT_PCM_16_BIT,
8 /* bufferCount */,
- NULL,
- NULL,
+ mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL,
+ mUseAudioCallback ? this : NULL,
(audio_output_flags_t)pcmFlags,
NULL,
true /* doNotReconnect */);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
index fbdf5bf..c2fea40 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.h
@@ -194,6 +194,7 @@ private:
int32_t mTotalBuffersQueued;
int32_t mLastAudioBufferDrained;
+ bool mUseAudioCallback;
sp<AWakeLock> mWakeLock;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 08045d1..3a98e8c 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -132,6 +132,7 @@ struct CodecObserver : public BnOMXObserver {
case omx_message::EMPTY_BUFFER_DONE:
{
msg->setInt32("buffer", omx_msg.u.buffer_data.buffer);
+ msg->setInt32("fence_fd", omx_msg.fenceFd);
break;
}
@@ -151,6 +152,8 @@ struct CodecObserver : public BnOMXObserver {
msg->setInt64(
"timestamp",
omx_msg.u.extended_buffer_data.timestamp);
+ msg->setInt32(
+ "fence_fd", omx_msg.fenceFd);
break;
}
@@ -199,13 +202,14 @@ protected:
private:
bool onOMXMessage(const sp<AMessage> &msg);
- bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID);
+ bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd);
bool onOMXFillBufferDone(
IOMX::buffer_id bufferID,
size_t rangeOffset, size_t rangeLength,
OMX_U32 flags,
- int64_t timeUs);
+ int64_t timeUs,
+ int fenceFd);
void getMoreInputDataIfPossible();
@@ -407,6 +411,38 @@ private:
////////////////////////////////////////////////////////////////////////////////
+void ACodec::BufferInfo::setWriteFence(int fenceFd, const char *dbg) {
+ if (mFenceFd >= 0) {
+ ALOGW("OVERWRITE OF %s fence %d by write fence %d in %s",
+ mIsReadFence ? "read" : "write", mFenceFd, fenceFd, dbg);
+ }
+ mFenceFd = fenceFd;
+ mIsReadFence = false;
+}
+
+void ACodec::BufferInfo::setReadFence(int fenceFd, const char *dbg) {
+ if (mFenceFd >= 0) {
+ ALOGW("OVERWRITE OF %s fence %d by read fence %d in %s",
+ mIsReadFence ? "read" : "write", mFenceFd, fenceFd, dbg);
+ }
+ mFenceFd = fenceFd;
+ mIsReadFence = true;
+}
+
+void ACodec::BufferInfo::checkWriteFence(const char *dbg) {
+ if (mFenceFd >= 0 && mIsReadFence) {
+ ALOGD("REUSING read fence %d as write fence in %s", mFenceFd, dbg);
+ }
+}
+
+void ACodec::BufferInfo::checkReadFence(const char *dbg) {
+ if (mFenceFd >= 0 && !mIsReadFence) {
+ ALOGD("REUSING write fence %d as read fence in %s", mFenceFd, dbg);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
ACodec::ACodec()
: mQuirks(0),
mNode(0),
@@ -640,11 +676,12 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) {
// cancel undequeued buffers to new surface
if (!storingMetadataInDecodedBuffers() || mLegacyAdaptiveExperiment) {
for (size_t i = 0; i < buffers.size(); ++i) {
- const BufferInfo &info = buffers[i];
+ BufferInfo &info = buffers.editItemAt(i);
if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
ALOGV("canceling buffer %p", info.mGraphicBuffer->getNativeBuffer());
err = nativeWindow->cancelBuffer(
- nativeWindow, info.mGraphicBuffer->getNativeBuffer(), -1);
+ nativeWindow, info.mGraphicBuffer->getNativeBuffer(), info.mFenceFd);
+ info.mFenceFd = -1;
if (err != OK) {
ALOGE("failed to cancel buffer %p to the new surface: %s (%d)",
info.mGraphicBuffer->getNativeBuffer(),
@@ -721,6 +758,7 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_US;
+ info.mFenceFd = -1;
uint32_t requiresAllocateBufferBit =
(portIndex == kPortIndexInput)
@@ -925,7 +963,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() {
// Dequeue buffers and send them to OMX
for (OMX_U32 i = 0; i < bufferCount; i++) {
ANativeWindowBuffer *buf;
- err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);
+ int fenceFd;
+ err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
if (err != 0) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
break;
@@ -934,6 +973,8 @@ status_t ACodec::allocateOutputBuffersFromNativeWindow() {
sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false));
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_US;
+ info.mFenceFd = fenceFd;
+ info.mIsReadFence = false;
info.mData = new ABuffer(NULL /* data */, bufferSize /* capacity */);
info.mGraphicBuffer = graphicBuffer;
mBuffers[kPortIndexOutput].push(info);
@@ -1004,6 +1045,7 @@ status_t ACodec::allocateOutputMetadataBuffers() {
for (OMX_U32 i = 0; i < bufferCount; i++) {
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
+ info.mFenceFd = -1;
info.mGraphicBuffer = NULL;
info.mDequeuedAt = mDequeueCounter;
@@ -1040,7 +1082,8 @@ status_t ACodec::allocateOutputMetadataBuffers() {
BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i);
ANativeWindowBuffer *buf;
- err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf);
+ int fenceFd;
+ err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
if (err != 0) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
break;
@@ -1050,6 +1093,7 @@ status_t ACodec::allocateOutputMetadataBuffers() {
mOMX->updateGraphicBufferInMeta(
mNode, kPortIndexOutput, graphicBuffer, info->mBufferID);
info->mStatus = BufferInfo::OWNED_BY_US;
+ info->setWriteFence(fenceFd, "allocateOutputMetadataBuffers for legacy");
info->mGraphicBuffer = graphicBuffer;
}
@@ -1083,7 +1127,9 @@ status_t ACodec::submitOutputMetadataBuffer() {
mComponentName.c_str(), info->mBufferID, info->mGraphicBuffer.get());
--mMetadataBuffersToSubmit;
- status_t err = mOMX->fillBuffer(mNode, info->mBufferID);
+ info->checkWriteFence("submitOutputMetadataBuffer");
+ status_t err = mOMX->fillBuffer(mNode, info->mBufferID, info->mFenceFd);
+ info->mFenceFd = -1;
if (err == OK) {
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
}
@@ -1091,6 +1137,16 @@ status_t ACodec::submitOutputMetadataBuffer() {
return err;
}
+status_t ACodec::waitForFence(int fd, const char *dbg ) {
+ status_t res = OK;
+ if (fd >= 0) {
+ sp<Fence> fence = new Fence(fd);
+ res = fence->wait(IOMX::kFenceTimeoutMs);
+ ALOGW_IF(res != OK, "FENCE TIMEOUT for %d in %s", fd, dbg);
+ }
+ return res;
+}
+
// static
const char *ACodec::_asString(BufferInfo::Status s) {
switch (s) {
@@ -1123,8 +1179,10 @@ status_t ACodec::cancelBufferToNativeWindow(BufferInfo *info) {
ALOGV("[%s] Calling cancelBuffer on buffer %u",
mComponentName.c_str(), info->mBufferID);
+ info->checkWriteFence("cancelBufferToNativeWindow");
int err = mNativeWindow->cancelBuffer(
- mNativeWindow.get(), info->mGraphicBuffer.get(), -1);
+ mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);
+ info->mFenceFd = -1;
ALOGW_IF(err != 0, "[%s] can not return buffer %u to native window",
mComponentName.c_str(), info->mBufferID);
@@ -1144,9 +1202,11 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
return NULL;
}
+ int fenceFd = -1;
do {
- if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) {
- ALOGE("dequeueBuffer failed.");
+ status_t err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
+ if (err != 0) {
+ ALOGE("dequeueBuffer failed: %s(%d).", asString(err), err);
return NULL;
}
@@ -1170,7 +1230,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
}
ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer());
info->mStatus = BufferInfo::OWNED_BY_US;
-
+ info->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow");
return info;
}
}
@@ -1213,6 +1273,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
// discard buffer in LRU info and replace with new buffer
oldest->mGraphicBuffer = new GraphicBuffer(buf, false);
oldest->mStatus = BufferInfo::OWNED_BY_US;
+ oldest->setWriteFence(fenceFd, "dequeueBufferFromNativeWindow for oldest");
mOMX->updateGraphicBufferInMeta(
mNode, kPortIndexOutput, oldest->mGraphicBuffer,
@@ -1224,7 +1285,7 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() {
ALOGV("replaced oldest buffer #%u with age %u (%p/%p stored in %p)",
(unsigned)(oldest - &mBuffers[kPortIndexOutput][0]),
mDequeueCounter - oldest->mDequeuedAt,
- grallocMeta->hHandle,
+ grallocMeta->pHandle,
oldest->mGraphicBuffer->handle, oldest->mData->base());
} else if (mOutputMetadataType == kMetadataBufferTypeANWBuffer) {
VideoNativeMetadata *nativeMeta =
@@ -1277,6 +1338,18 @@ status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
BufferInfo *info = &mBuffers[portIndex].editItemAt(i);
status_t err = OK;
+ // there should not be any fences in the metadata
+ MetadataBufferType type =
+ portIndex == kPortIndexOutput ? mOutputMetadataType : mInputMetadataType;
+ if (type == kMetadataBufferTypeANWBuffer && info->mData != NULL
+ && info->mData->size() >= sizeof(VideoNativeMetadata)) {
+ int fenceFd = ((VideoNativeMetadata *)info->mData->data())->nFenceFd;
+ if (fenceFd >= 0) {
+ ALOGW("unreleased fence (%d) in %s metadata buffer %zu",
+ fenceFd, portIndex == kPortIndexInput ? "input" : "output", i);
+ }
+ }
+
switch (info->mStatus) {
case BufferInfo::OWNED_BY_US:
if (portIndex == kPortIndexOutput && mNativeWindow != NULL) {
@@ -1294,6 +1367,10 @@ status_t ACodec::freeBuffer(OMX_U32 portIndex, size_t i) {
break;
}
+ if (info->mFenceFd >= 0) {
+ ::close(info->mFenceFd);
+ }
+
// remove buffer even if mOMX->freeBuffer fails
mBuffers[portIndex].removeAt(i);
@@ -4433,9 +4510,12 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
case omx_message::EMPTY_BUFFER_DONE:
{
IOMX::buffer_id bufferID;
+ int32_t fenceFd;
+
CHECK(msg->findInt32("buffer", (int32_t*)&bufferID));
+ CHECK(msg->findInt32("fence_fd", &fenceFd));
- return onOMXEmptyBufferDone(bufferID);
+ return onOMXEmptyBufferDone(bufferID, fenceFd);
}
case omx_message::FILL_BUFFER_DONE:
@@ -4443,19 +4523,21 @@ bool ACodec::BaseState::onOMXMessage(const sp<AMessage> &msg) {
IOMX::buffer_id bufferID;
CHECK(msg->findInt32("buffer", (int32_t*)&bufferID));
- int32_t rangeOffset, rangeLength, flags;
+ int32_t rangeOffset, rangeLength, flags, fenceFd;
int64_t timeUs;
CHECK(msg->findInt32("range_offset", &rangeOffset));
CHECK(msg->findInt32("range_length", &rangeLength));
CHECK(msg->findInt32("flags", &flags));
CHECK(msg->findInt64("timestamp", &timeUs));
+ CHECK(msg->findInt32("fence_fd", &fenceFd));
return onOMXFillBufferDone(
bufferID,
(size_t)rangeOffset, (size_t)rangeLength,
(OMX_U32)flags,
- timeUs);
+ timeUs,
+ fenceFd);
}
default:
@@ -4486,7 +4568,7 @@ bool ACodec::BaseState::onOMXEvent(
return true;
}
-bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
+bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd) {
ALOGV("[%s] onOMXEmptyBufferDone %u",
mCodec->mComponentName.c_str(), bufferID);
@@ -4495,10 +4577,20 @@ bool ACodec::BaseState::onOMXEmptyBufferDone(IOMX::buffer_id bufferID) {
if (status != BufferInfo::OWNED_BY_COMPONENT) {
ALOGE("Wrong ownership in EBD: %s(%d) buffer #%u", _asString(status), status, bufferID);
mCodec->dumpBuffers(kPortIndexInput);
+ if (fenceFd >= 0) {
+ ::close(fenceFd);
+ }
return false;
}
info->mStatus = BufferInfo::OWNED_BY_US;
+ // input buffers cannot take fences, so wait for any fence now
+ (void)mCodec->waitForFence(fenceFd, "onOMXEmptyBufferDone");
+ fenceFd = -1;
+
+ // still save fence for completeness
+ info->setWriteFence(fenceFd, "onOMXEmptyBufferDone");
+
// 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
@@ -4670,13 +4762,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
mCodec->submitOutputMetadataBuffer();
}
}
+ info->checkReadFence("onInputBufferFilled");
status_t err2 = mCodec->mOMX->emptyBuffer(
mCodec->mNode,
bufferID,
0,
buffer->size(),
flags,
- timeUs);
+ timeUs,
+ info->mFenceFd);
+ info->mFenceFd = -1;
if (err2 != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2));
return;
@@ -4704,13 +4799,16 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
ALOGV("[%s] calling emptyBuffer %u signalling EOS",
mCodec->mComponentName.c_str(), bufferID);
+ info->checkReadFence("onInputBufferFilled");
status_t err2 = mCodec->mOMX->emptyBuffer(
mCodec->mNode,
bufferID,
0,
0,
OMX_BUFFERFLAG_EOS,
- 0);
+ 0,
+ info->mFenceFd);
+ info->mFenceFd = -1;
if (err2 != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err2));
return;
@@ -4765,7 +4863,8 @@ bool ACodec::BaseState::onOMXFillBufferDone(
IOMX::buffer_id bufferID,
size_t rangeOffset, size_t rangeLength,
OMX_U32 flags,
- int64_t timeUs) {
+ int64_t timeUs,
+ int fenceFd) {
ALOGV("[%s] onOMXFillBufferDone %u time %" PRId64 " us, flags = 0x%08x",
mCodec->mComponentName.c_str(), bufferID, timeUs, flags);
@@ -4794,12 +4893,22 @@ bool ACodec::BaseState::onOMXFillBufferDone(
ALOGE("Wrong ownership in FBD: %s(%d) buffer #%u", _asString(status), status, bufferID);
mCodec->dumpBuffers(kPortIndexOutput);
mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
+ if (fenceFd >= 0) {
+ ::close(fenceFd);
+ }
return true;
}
info->mDequeuedAt = ++mCodec->mDequeueCounter;
info->mStatus = BufferInfo::OWNED_BY_US;
+ // byte buffers cannot take fences, so wait for any fence now
+ if (mCodec->mNativeWindow == NULL) {
+ (void)mCodec->waitForFence(fenceFd, "onOMXFillBufferDone");
+ fenceFd = -1;
+ }
+ info->setReadFence(fenceFd, "onOMXFillBufferDone");
+
PortMode mode = getPortMode(kPortIndexOutput);
switch (mode) {
@@ -4813,7 +4922,8 @@ bool ACodec::BaseState::onOMXFillBufferDone(
ALOGV("[%s] calling fillBuffer %u",
mCodec->mComponentName.c_str(), info->mBufferID);
- err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID);
+ err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID, info->mFenceFd);
+ info->mFenceFd = -1;
if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
return true;
@@ -4835,7 +4945,7 @@ bool ACodec::BaseState::onOMXFillBufferDone(
VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)info->mData->data();
if (info->mData->size() >= sizeof(grallocMeta)
&& grallocMeta.eType == kMetadataBufferTypeGrallocSource) {
- handle = (native_handle_t *)grallocMeta.hHandle;
+ handle = (native_handle_t *)grallocMeta.pHandle;
} else if (info->mData->size() >= sizeof(nativeMeta)
&& nativeMeta.eType == kMetadataBufferTypeANWBuffer) {
handle = (native_handle_t *)nativeMeta.pBuffer->handle;
@@ -4946,17 +5056,23 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
err = native_window_set_buffers_timestamp(mCodec->mNativeWindow.get(), timestampNs);
ALOGW_IF(err != NO_ERROR, "failed to set buffer timestamp: %d", err);
+ info->checkReadFence("onOutputBufferDrained before queueBuffer");
err = mCodec->mNativeWindow->queueBuffer(
- mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), -1);
+ mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);
+ info->mFenceFd = -1;
if (err == OK) {
info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
} else {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
info->mStatus = BufferInfo::OWNED_BY_US;
+ // keeping read fence as write fence to avoid clobbering
+ info->mIsReadFence = false;
}
} else {
if (mCodec->mNativeWindow != NULL &&
(info->mData == NULL || info->mData->size() != 0)) {
+ // move read fence into write fence to avoid clobbering
+ info->mIsReadFence = false;
ATRACE_NAME("frame-drop");
}
info->mStatus = BufferInfo::OWNED_BY_US;
@@ -4991,7 +5107,10 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
if (info != NULL) {
ALOGV("[%s] calling fillBuffer %u",
mCodec->mComponentName.c_str(), info->mBufferID);
- status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID);
+ info->checkWriteFence("onOutputBufferDrained::RESUBMIT_BUFFERS");
+ status_t err = mCodec->mOMX->fillBuffer(
+ mCodec->mNode, info->mBufferID, info->mFenceFd);
+ info->mFenceFd = -1;
if (err == OK) {
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
} else {
@@ -5754,7 +5873,9 @@ void ACodec::ExecutingState::submitRegularOutputBuffers() {
ALOGV("[%s] calling fillBuffer %u", mCodec->mComponentName.c_str(), info->mBufferID);
- status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID);
+ info->checkWriteFence("submitRegularOutputBuffers");
+ status_t err = mCodec->mOMX->fillBuffer(mCodec->mNode, info->mBufferID, info->mFenceFd);
+ info->mFenceFd = -1;
if (err != OK) {
failed = true;
break;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 5d04628..e69890d 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -125,13 +125,13 @@ struct MuxOMX : public IOMX {
virtual status_t freeBuffer(
node_id node, OMX_U32 port_index, buffer_id buffer);
- virtual status_t fillBuffer(node_id node, buffer_id buffer);
+ virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd);
virtual status_t emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp);
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
virtual status_t getExtensionIndex(
node_id node,
@@ -385,17 +385,17 @@ status_t MuxOMX::freeBuffer(
return getOMX(node)->freeBuffer(node, port_index, buffer);
}
-status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer) {
- return getOMX(node)->fillBuffer(node, buffer);
+status_t MuxOMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) {
+ return getOMX(node)->fillBuffer(node, buffer, fenceFd);
}
status_t MuxOMX::emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
return getOMX(node)->emptyBuffer(
- node, buffer, range_offset, range_length, flags, timestamp);
+ node, buffer, range_offset, range_length, flags, timestamp, fenceFd);
}
status_t MuxOMX::getExtensionIndex(
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 413628d..6828b54 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -601,16 +601,18 @@ void convertMessageToMetaData(const sp<AMessage> &msg, sp<MetaData> &meta) {
// reassemble the csd data into its original form
sp<ABuffer> csd0;
if (msg->findBuffer("csd-0", &csd0)) {
- if (mime.startsWith("video/")) { // do we need to be stricter than this?
+ if (mime == MEDIA_MIMETYPE_VIDEO_AVC) {
sp<ABuffer> csd1;
if (msg->findBuffer("csd-1", &csd1)) {
char avcc[1024]; // that oughta be enough, right?
size_t outsize = reassembleAVCC(csd0, csd1, avcc);
meta->setData(kKeyAVCC, kKeyAVCC, avcc, outsize);
}
- } else if (mime.startsWith("audio/")) {
+ } else if (mime == MEDIA_MIMETYPE_AUDIO_AAC || mime == MEDIA_MIMETYPE_VIDEO_MPEG4) {
int csd0size = csd0->size();
char esds[csd0size + 31];
+ // The written ESDS is actually for an audio stream, but it's enough
+ // for transporting the CSD to muxers.
reassembleESDS(csd0, esds);
meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds));
}
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index 08e956a..8ac337a 100644..100755
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -121,6 +121,7 @@ SoftAVC::SoftAVC(
mIvColorFormat(IV_YUV_420P),
mNewWidth(mWidth),
mNewHeight(mHeight),
+ mNewLevel(0),
mChangingResolution(false) {
initPorts(
kNumBuffers, INPUT_BUF_SIZE, kNumBuffers, CODEC_MIME_TYPE);
@@ -303,18 +304,22 @@ status_t SoftAVC::initDecoder() {
uint32_t displayHeight = outputBufferHeight();
uint32_t displaySizeY = displayStride * displayHeight;
- if (displaySizeY > (1920 * 1088)) {
- i4_level = 50;
- } else if (displaySizeY > (1280 * 720)) {
- i4_level = 40;
- } else if (displaySizeY > (720 * 576)) {
- i4_level = 31;
- } else if (displaySizeY > (624 * 320)) {
- i4_level = 30;
- } else if (displaySizeY > (352 * 288)) {
- i4_level = 21;
+ if(mNewLevel == 0){
+ if (displaySizeY > (1920 * 1088)) {
+ i4_level = 50;
+ } else if (displaySizeY > (1280 * 720)) {
+ i4_level = 40;
+ } else if (displaySizeY > (720 * 576)) {
+ i4_level = 31;
+ } else if (displaySizeY > (624 * 320)) {
+ i4_level = 30;
+ } else if (displaySizeY > (352 * 288)) {
+ i4_level = 21;
+ } else {
+ i4_level = 20;
+ }
} else {
- i4_level = 20;
+ i4_level = mNewLevel;
}
{
@@ -691,6 +696,7 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
bool unsupportedDimensions =
(IVD_STREAM_WIDTH_HEIGHT_NOT_SUPPORTED == (s_dec_op.u4_error_code & 0xFF));
bool resChanged = (IVD_RES_CHANGED == (s_dec_op.u4_error_code & 0xFF));
+ bool unsupportedLevel = (IH264D_UNSUPPORTED_LEVEL == (s_dec_op.u4_error_code & 0xFF));
GETTIME(&mTimeEnd, NULL);
/* Compute time taken for decode() */
@@ -722,6 +728,18 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
return;
}
+ if (unsupportedLevel && !mFlushNeeded) {
+
+ mNewLevel = 51;
+
+ CHECK_EQ(reInitDecoder(), (status_t)OK);
+
+ setDecodeArgs(&s_dec_ip, &s_dec_op, inHeader, outHeader, timeStampIx);
+
+ ivdec_api_function(mCodecCtx, (void *)&s_dec_ip, (void *)&s_dec_op);
+ return;
+ }
+
// If the decoder is in the changing resolution mode and there is no output present,
// that means the switching is done and it's ready to reset the decoder and the plugin.
if (mChangingResolution && !s_dec_op.u4_output_present) {
@@ -745,6 +763,17 @@ void SoftAVC::onQueueFilled(OMX_U32 portIndex) {
continue;
}
+ if (unsupportedLevel) {
+
+ if (mFlushNeeded) {
+ setFlushMode();
+ }
+
+ mNewLevel = 51;
+ mInitNeeded = true;
+ continue;
+ }
+
if ((0 < s_dec_op.u4_pic_wd) && (0 < s_dec_op.u4_pic_ht)) {
uint32_t width = s_dec_op.u4_pic_wd;
uint32_t height = s_dec_op.u4_pic_ht;
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.h b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
index 191a71d..2067810 100644..100755
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.h
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.h
@@ -100,6 +100,7 @@ private:
bool mInitNeeded;
uint32_t mNewWidth;
uint32_t mNewHeight;
+ uint32_t mNewLevel;
// The input stream has changed to a different resolution, which is still supported by the
// codec. So the codec is switching to decode the new resolution.
bool mChangingResolution;
diff --git a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
index 6322dc2..7ff9ee7 100644
--- a/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
+++ b/media/libstagefright/codecs/opus/dec/SoftOpus.cpp
@@ -403,6 +403,14 @@ void SoftOpus::onQueueFilled(OMX_U32 portIndex) {
BufferInfo *inInfo = *inQueue.begin();
OMX_BUFFERHEADERTYPE *inHeader = inInfo->mHeader;
+ // Ignore CSD re-submissions.
+ if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ inQueue.erase(inQueue.begin());
+ inInfo->mOwnedByUs = false;
+ notifyEmptyBufferDone(inHeader);
+ return;
+ }
+
BufferInfo *outInfo = *outQueue.begin();
OMX_BUFFERHEADERTYPE *outHeader = outInfo->mHeader;
diff --git a/media/libstagefright/include/OMX.h b/media/libstagefright/include/OMX.h
index c34954b..d468dfc 100644
--- a/media/libstagefright/include/OMX.h
+++ b/media/libstagefright/include/OMX.h
@@ -118,13 +118,13 @@ public:
virtual status_t freeBuffer(
node_id node, OMX_U32 port_index, buffer_id buffer);
- virtual status_t fillBuffer(node_id node, buffer_id buffer);
+ virtual status_t fillBuffer(node_id node, buffer_id buffer, int fenceFd);
virtual status_t emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp);
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
virtual status_t getExtensionIndex(
node_id node,
@@ -148,10 +148,10 @@ public:
OMX_IN OMX_PTR pEventData);
OMX_ERRORTYPE OnEmptyBufferDone(
- node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+ node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd);
OMX_ERRORTYPE OnFillBufferDone(
- node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+ node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd);
void invalidateNodeID(node_id node);
diff --git a/media/libstagefright/include/OMXNodeInstance.h b/media/libstagefright/include/OMXNodeInstance.h
index fe6dccd..76df815 100644
--- a/media/libstagefright/include/OMXNodeInstance.h
+++ b/media/libstagefright/include/OMXNodeInstance.h
@@ -105,16 +105,16 @@ struct OMXNodeInstance {
status_t freeBuffer(OMX_U32 portIndex, OMX::buffer_id buffer);
- status_t fillBuffer(OMX::buffer_id buffer);
+ status_t fillBuffer(OMX::buffer_id buffer, int fenceFd);
status_t emptyBuffer(
OMX::buffer_id buffer,
OMX_U32 rangeOffset, OMX_U32 rangeLength,
- OMX_U32 flags, OMX_TICKS timestamp);
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
status_t emptyGraphicBuffer(
OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &buffer,
- OMX_U32 flags, OMX_TICKS timestamp);
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd);
status_t getExtensionIndex(
const char *parameterName, OMX_INDEXTYPE *index);
@@ -208,9 +208,18 @@ private:
status_t storeMetaDataInBuffers_l(
OMX_U32 portIndex, OMX_BOOL enable, MetadataBufferType *type);
+ // Stores fence into buffer if it is ANWBuffer type and has enough space.
+ // otherwise, waits for the fence to signal. Takes ownership of |fenceFd|.
+ status_t storeFenceInMeta_l(
+ OMX_BUFFERHEADERTYPE *header, int fenceFd, OMX_U32 portIndex);
+
+ // Retrieves the fence from buffer if ANWBuffer type and has enough space. Otherwise, returns -1
+ int retrieveFenceFromMeta_l(
+ OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex);
+
status_t emptyBuffer_l(
OMX_BUFFERHEADERTYPE *header,
- OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr);
+ OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr, int fenceFd);
status_t updateGraphicBufferInMeta_l(
OMX_U32 portIndex, const sp<GraphicBuffer> &graphicBuffer,
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index f797e63..d30bba5 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -64,6 +64,7 @@ GraphicBufferSource::GraphicBufferSource(
mLatestBufferId(-1),
mLatestBufferFrameNum(0),
mLatestBufferUseCount(0),
+ mLatestBufferFence(Fence::NO_FENCE),
mRepeatBufferDeferred(false),
mTimePerCaptureUs(-1ll),
mTimePerFrameUs(-1ll),
@@ -226,9 +227,8 @@ void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) {
mCodecBuffers.add(codecBuffer);
}
-void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
+void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd) {
Mutex::Autolock autoLock(mMutex);
-
if (!mExecuting) {
return;
}
@@ -237,6 +237,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
if (cbi < 0) {
// This should never happen.
ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header);
+ if (fenceFd >= 0) {
+ ::close(fenceFd);
+ }
return;
}
@@ -258,6 +261,9 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
}
// No GraphicBuffer to deal with, no additional input or output is
// expected, so just return.
+ if (fenceFd >= 0) {
+ ::close(fenceFd);
+ }
return;
}
@@ -269,10 +275,10 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
if (type == kMetadataBufferTypeGrallocSource
&& header->nAllocLen >= sizeof(VideoGrallocMetadata)) {
VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)data;
- if (grallocMeta.hHandle != codecBuffer.mGraphicBuffer->handle) {
+ if (grallocMeta.pHandle != codecBuffer.mGraphicBuffer->handle) {
// should never happen
ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p",
- grallocMeta.hHandle, codecBuffer.mGraphicBuffer->handle);
+ grallocMeta.pHandle, codecBuffer.mGraphicBuffer->handle);
CHECK(!"codecBufferEmptied: mismatched buffer");
}
} else if (type == kMetadataBufferTypeANWBuffer
@@ -291,6 +297,7 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
// If we find a match, release that slot. If we don't, the BufferQueue
// has dropped that GraphicBuffer, and there's nothing for us to release.
int id = codecBuffer.mBuf;
+ sp<Fence> fence = new Fence(fenceFd);
if (mBufferSlot[id] != NULL &&
mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) {
ALOGV("cbi %d matches bq slot %d, handle=%p",
@@ -304,15 +311,16 @@ void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) {
int outSlot;
mConsumer->attachBuffer(&outSlot, mBufferSlot[id]);
mConsumer->releaseBuffer(outSlot, 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence);
} else {
mConsumer->releaseBuffer(id, codecBuffer.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, fence);
}
}
} else {
ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d",
cbi);
+ // we will not reuse codec buffer, so there is no need to wait for fence
}
// Mark the codec buffer as available by clearing the GraphicBuffer ref.
@@ -394,7 +402,7 @@ void GraphicBufferSource::suspend(bool suspend) {
mConsumer->detachBuffer(item.mBuf);
mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
mConsumer->releaseBuffer(item.mBuf, 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
} else {
mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
@@ -447,13 +455,6 @@ bool GraphicBufferSource::fillCodecBuffer_l() {
mNumFramesAvailable--;
- // Wait for it to become available.
- err = item.mFence->waitForever("GraphicBufferSource::fillCodecBuffer_l");
- if (err != OK) {
- ALOGW("failed to wait for buffer fence: %d", err);
- // keep going
- }
-
// If this is the first time we're seeing this buffer, add it to our
// slot table.
if (item.mGraphicBuffer != NULL) {
@@ -489,11 +490,12 @@ bool GraphicBufferSource::fillCodecBuffer_l() {
mConsumer->detachBuffer(item.mBuf);
mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
mConsumer->releaseBuffer(item.mBuf, 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
} else {
mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
}
+ // item.mFence is released at the end of this method
} else {
ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi);
setLatestBuffer_l(item, dropped);
@@ -520,9 +522,10 @@ bool GraphicBufferSource::repeatLatestBuffer_l() {
mLatestBufferFrameNum,
EGL_NO_DISPLAY,
EGL_NO_SYNC_KHR,
- Fence::NO_FENCE);
+ mLatestBufferFence);
mLatestBufferId = -1;
mLatestBufferFrameNum = 0;
+ mLatestBufferFence = Fence::NO_FENCE;
return false;
}
@@ -537,6 +540,7 @@ bool GraphicBufferSource::repeatLatestBuffer_l() {
item.mBuf = mLatestBufferId;
item.mFrameNumber = mLatestBufferFrameNum;
item.mTimestamp = mRepeatLastFrameTimestamp;
+ item.mFence = mLatestBufferFence;
status_t err = submitBuffer_l(item, cbi);
@@ -576,12 +580,13 @@ void GraphicBufferSource::setLatestBuffer_l(
mConsumer->attachBuffer(&outSlot, mBufferSlot[mLatestBufferId]);
mConsumer->releaseBuffer(outSlot, 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, mLatestBufferFence);
} else {
mConsumer->releaseBuffer(
mLatestBufferId, mLatestBufferFrameNum,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, mLatestBufferFence);
}
+ // mLatestBufferFence will be set to new fence just below
}
}
@@ -592,6 +597,7 @@ void GraphicBufferSource::setLatestBuffer_l(
mLatestBufferUseCount = dropped ? 0 : 1;
mRepeatBufferDeferred = false;
mRepeatLastFrameCount = kRepeatLastFrameCount;
+ mLatestBufferFence = item.mFence;
if (mReflector != NULL) {
sp<AMessage> msg = new AMessage(kWhatRepeatLastFrame, mReflector);
@@ -687,8 +693,7 @@ int64_t GraphicBufferSource::getTimestamp(const BufferItem &item) {
return timeUs;
}
-status_t GraphicBufferSource::submitBuffer_l(
- const BufferItem &item, int cbi) {
+status_t GraphicBufferSource::submitBuffer_l(const BufferItem &item, int cbi) {
ALOGV("submitBuffer_l cbi=%d", cbi);
int64_t timeUs = getTimestamp(item);
@@ -704,7 +709,8 @@ status_t GraphicBufferSource::submitBuffer_l(
OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
sp<GraphicBuffer> buffer = codecBuffer.mGraphicBuffer;
status_t err = mNodeInstance->emptyGraphicBuffer(
- header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs);
+ header, buffer, OMX_BUFFERFLAG_ENDOFFRAME, timeUs,
+ item.mFence->isValid() ? item.mFence->dup() : -1);
if (err != OK) {
ALOGW("WARNING: emptyNativeWindowBuffer failed: 0x%x", err);
codecBuffer.mGraphicBuffer = NULL;
@@ -737,7 +743,7 @@ void GraphicBufferSource::submitEndOfInputStream_l() {
OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader;
status_t err = mNodeInstance->emptyGraphicBuffer(
header, NULL /* buffer */, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS,
- 0 /* timestamp */);
+ 0 /* timestamp */, -1 /* fenceFd */);
if (err != OK) {
ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
} else {
@@ -799,7 +805,7 @@ void GraphicBufferSource::onFrameAvailable(const BufferItem& /*item*/) {
mConsumer->detachBuffer(item.mBuf);
mConsumer->attachBuffer(&item.mBuf, item.mGraphicBuffer);
mConsumer->releaseBuffer(item.mBuf, 0,
- EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE);
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
} else {
mConsumer->releaseBuffer(item.mBuf, item.mFrameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence);
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 21ee96a..555bbec 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -93,7 +93,7 @@ public:
// Called from OnEmptyBufferDone. If we have a BQ buffer available,
// fill it with a new frame of data; otherwise, just mark it as available.
- void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header);
+ void codecBufferEmptied(OMX_BUFFERHEADERTYPE* header, int fenceFd);
// Called when omx_message::FILL_BUFFER_DONE is received. (Currently the
// buffer source will fix timestamp in the header if needed.)
@@ -274,6 +274,7 @@ private:
int mLatestBufferId;
uint64_t mLatestBufferFrameNum;
int32_t mLatestBufferUseCount;
+ sp<Fence> mLatestBufferFence;
// The previous buffer should've been repeated but
// no codec buffer was available at the time.
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 4ca827c..76217ec 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -415,17 +415,17 @@ status_t OMX::freeBuffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
port_index, buffer);
}
-status_t OMX::fillBuffer(node_id node, buffer_id buffer) {
- return findInstance(node)->fillBuffer(buffer);
+status_t OMX::fillBuffer(node_id node, buffer_id buffer, int fenceFd) {
+ return findInstance(node)->fillBuffer(buffer, fenceFd);
}
status_t OMX::emptyBuffer(
node_id node,
buffer_id buffer,
OMX_U32 range_offset, OMX_U32 range_length,
- OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
return findInstance(node)->emptyBuffer(
- buffer, range_offset, range_length, flags, timestamp);
+ buffer, range_offset, range_length, flags, timestamp, fenceFd);
}
status_t OMX::getExtensionIndex(
@@ -459,6 +459,7 @@ OMX_ERRORTYPE OMX::OnEvent(
omx_message msg;
msg.type = omx_message::EVENT;
msg.node = node;
+ msg.fenceFd = -1;
msg.u.event_data.event = eEvent;
msg.u.event_data.data1 = nData1;
msg.u.event_data.data2 = nData2;
@@ -469,12 +470,13 @@ OMX_ERRORTYPE OMX::OnEvent(
}
OMX_ERRORTYPE OMX::OnEmptyBufferDone(
- node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) {
ALOGV("OnEmptyBufferDone buffer=%p", pBuffer);
omx_message msg;
msg.type = omx_message::EMPTY_BUFFER_DONE;
msg.node = node;
+ msg.fenceFd = fenceFd;
msg.u.buffer_data.buffer = buffer;
findDispatcher(node)->post(msg);
@@ -483,12 +485,13 @@ OMX_ERRORTYPE OMX::OnEmptyBufferDone(
}
OMX_ERRORTYPE OMX::OnFillBufferDone(
- node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+ node_id node, buffer_id buffer, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer, int fenceFd) {
ALOGV("OnFillBufferDone buffer=%p", pBuffer);
omx_message msg;
msg.type = omx_message::FILL_BUFFER_DONE;
msg.node = node;
+ msg.fenceFd = fenceFd;
msg.u.extended_buffer_data.buffer = buffer;
msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index e4b2de4..9e399f9 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -76,11 +76,11 @@ static const OMX_U32 kPortIndexOutput = 1;
#define SIMPLE_NEW_BUFFER(buffer_id, port, size, data) \
NEW_BUFFER_FMT(buffer_id, port, "%zu@%p", (size), (data))
-#define EMPTY_BUFFER(addr, header) "%#x [%u@%p]", \
- (addr), (header)->nAllocLen, (header)->pBuffer
-#define FULL_BUFFER(addr, header) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld]", \
+#define EMPTY_BUFFER(addr, header, fenceFd) "%#x [%u@%p fc=%d]", \
+ (addr), (header)->nAllocLen, (header)->pBuffer, (fenceFd)
+#define FULL_BUFFER(addr, header, fenceFd) "%#" PRIxPTR " [%u@%p (%u..+%u) f=%x ts=%lld fc=%d]", \
(intptr_t)(addr), (header)->nAllocLen, (header)->pBuffer, \
- (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp
+ (header)->nOffset, (header)->nFilledLen, (header)->nFlags, (header)->nTimeStamp, (fenceFd)
#define WITH_STATS_WRAPPER(fmt, ...) fmt " { IN=%zu/%zu OUT=%zu/%zu }", ##__VA_ARGS__, \
mInputBuffersWithCodec.size(), mNumPortBuffers[kPortIndexInput], \
@@ -812,7 +812,7 @@ status_t OMXNodeInstance::updateGraphicBufferInMeta_l(
&& header->nAllocLen >= sizeof(VideoGrallocMetadata)) {
VideoGrallocMetadata &metadata = *(VideoGrallocMetadata *)(header->pBuffer);
metadata.eType = kMetadataBufferTypeGrallocSource;
- metadata.hHandle = graphicBuffer == NULL ? NULL : graphicBuffer->handle;
+ metadata.pHandle = graphicBuffer == NULL ? NULL : graphicBuffer->handle;
} else if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer
&& header->nAllocLen >= sizeof(VideoNativeMetadata)) {
VideoNativeMetadata &metadata = *(VideoNativeMetadata *)(header->pBuffer);
@@ -1050,7 +1050,7 @@ status_t OMXNodeInstance::freeBuffer(
return StatusFromOMXError(err);
}
-status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) {
+status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer, int fenceFd) {
Mutex::Autolock autoLock(mLock);
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer);
@@ -1058,15 +1058,22 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) {
header->nOffset = 0;
header->nFlags = 0;
+ // meta now owns fenceFd
+ status_t res = storeFenceInMeta_l(header, fenceFd, kPortIndexOutput);
+ if (res != OK) {
+ CLOG_ERROR(fillBuffer::storeFenceInMeta, res, EMPTY_BUFFER(buffer, header, fenceFd));
+ return res;
+ }
+
{
Mutex::Autolock _l(mDebugLock);
mOutputBuffersWithCodec.add(header);
- CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header)));
+ CLOG_BUMPED_BUFFER(fillBuffer, WITH_STATS(EMPTY_BUFFER(buffer, header, fenceFd)));
}
OMX_ERRORTYPE err = OMX_FillThisBuffer(mHandle, header);
if (err != OMX_ErrorNone) {
- CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header));
+ CLOG_ERROR(fillBuffer, err, EMPTY_BUFFER(buffer, header, fenceFd));
Mutex::Autolock _l(mDebugLock);
mOutputBuffersWithCodec.remove(header);
}
@@ -1076,7 +1083,7 @@ status_t OMXNodeInstance::fillBuffer(OMX::buffer_id buffer) {
status_t OMXNodeInstance::emptyBuffer(
OMX::buffer_id buffer,
OMX_U32 rangeOffset, OMX_U32 rangeLength,
- OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
Mutex::Autolock autoLock(mLock);
OMX_BUFFERHEADERTYPE *header = findBufferHeader(buffer);
@@ -1084,6 +1091,9 @@ status_t OMXNodeInstance::emptyBuffer(
// corner case: we permit rangeOffset == end-of-buffer with rangeLength == 0.
if (rangeOffset > header->nAllocLen
|| rangeLength > header->nAllocLen - rangeOffset) {
+ if (fenceFd >= 0) {
+ ::close(fenceFd);
+ }
return BAD_VALUE;
}
header->nFilledLen = rangeLength;
@@ -1103,14 +1113,14 @@ status_t OMXNodeInstance::emptyBuffer(
VideoNativeMetadata &backupMeta = *(VideoNativeMetadata *)backup->base();
VideoGrallocMetadata &codecMeta = *(VideoGrallocMetadata *)codec->base();
ALOGV("converting ANWB %p to handle %p", backupMeta.pBuffer, backupMeta.pBuffer->handle);
- codecMeta.hHandle = backupMeta.pBuffer->handle;
+ codecMeta.pHandle = backupMeta.pBuffer->handle;
codecMeta.eType = kMetadataBufferTypeGrallocSource;
header->nFilledLen = sizeof(codecMeta);
} else {
buffer_meta->CopyToOMX(header);
}
- return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer);
+ return emptyBuffer_l(header, flags, timestamp, (intptr_t)buffer, fenceFd);
}
// log queued buffer activity for the next few input and/or output frames
@@ -1137,11 +1147,62 @@ void OMXNodeInstance::unbumpDebugLevel_l(size_t portIndex) {
}
}
+status_t OMXNodeInstance::storeFenceInMeta_l(
+ OMX_BUFFERHEADERTYPE *header, int fenceFd, OMX_U32 portIndex) {
+ // propagate fence if component supports it; wait for it otherwise
+ OMX_U32 metaSize = portIndex == kPortIndexInput ? header->nFilledLen : header->nAllocLen;
+ if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer
+ && metaSize >= sizeof(VideoNativeMetadata)) {
+ VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)(header->pBuffer);
+ if (nativeMeta.nFenceFd >= 0) {
+ ALOGE("fence (%d) already exists in meta", nativeMeta.nFenceFd);
+ if (fenceFd >= 0) {
+ ::close(fenceFd);
+ }
+ return ALREADY_EXISTS;
+ }
+ nativeMeta.nFenceFd = fenceFd;
+ } else if (fenceFd >= 0) {
+ CLOG_BUFFER(storeFenceInMeta, "waiting for fence %d", fenceFd);
+ sp<Fence> fence = new Fence(fenceFd);
+ return fence->wait(IOMX::kFenceTimeoutMs);
+ }
+ return OK;
+}
+
+int OMXNodeInstance::retrieveFenceFromMeta_l(
+ OMX_BUFFERHEADERTYPE *header, OMX_U32 portIndex) {
+ OMX_U32 metaSize = portIndex == kPortIndexInput ? header->nAllocLen : header->nFilledLen;
+ int fenceFd = -1;
+ if (mMetadataType[portIndex] == kMetadataBufferTypeANWBuffer
+ && header->nAllocLen >= sizeof(VideoNativeMetadata)) {
+ VideoNativeMetadata &nativeMeta = *(VideoNativeMetadata *)(header->pBuffer);
+ if (nativeMeta.eType == kMetadataBufferTypeANWBuffer) {
+ fenceFd = nativeMeta.nFenceFd;
+ nativeMeta.nFenceFd = -1;
+ }
+ if (metaSize < sizeof(nativeMeta) && fenceFd >= 0) {
+ CLOG_ERROR(foundFenceInEmptyMeta, BAD_VALUE, FULL_BUFFER(
+ NULL, header, nativeMeta.nFenceFd));
+ fenceFd = -1;
+ }
+ }
+ return fenceFd;
+}
+
status_t OMXNodeInstance::emptyBuffer_l(
- OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp, intptr_t debugAddr) {
+ OMX_BUFFERHEADERTYPE *header, OMX_U32 flags, OMX_TICKS timestamp,
+ intptr_t debugAddr, int fenceFd) {
header->nFlags = flags;
header->nTimeStamp = timestamp;
+ status_t res = storeFenceInMeta_l(header, fenceFd, kPortIndexInput);
+ if (res != OK) {
+ CLOG_ERROR(emptyBuffer::storeFenceInMeta, res, WITH_STATS(
+ FULL_BUFFER(debugAddr, header, fenceFd)));
+ return res;
+ }
+
{
Mutex::Autolock _l(mDebugLock);
mInputBuffersWithCodec.add(header);
@@ -1151,11 +1212,11 @@ status_t OMXNodeInstance::emptyBuffer_l(
bumpDebugLevel_l(2 /* numInputBuffers */, 0 /* numOutputBuffers */);
}
- CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header)));
+ CLOG_BUMPED_BUFFER(emptyBuffer, WITH_STATS(FULL_BUFFER(debugAddr, header, fenceFd)));
}
OMX_ERRORTYPE err = OMX_EmptyThisBuffer(mHandle, header);
- CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header));
+ CLOG_IF_ERROR(emptyBuffer, err, FULL_BUFFER(debugAddr, header, fenceFd));
{
Mutex::Autolock _l(mDebugLock);
@@ -1172,18 +1233,19 @@ status_t OMXNodeInstance::emptyBuffer_l(
// like emptyBuffer, but the data is already in header->pBuffer
status_t OMXNodeInstance::emptyGraphicBuffer(
OMX_BUFFERHEADERTYPE *header, const sp<GraphicBuffer> &graphicBuffer,
- OMX_U32 flags, OMX_TICKS timestamp) {
+ OMX_U32 flags, OMX_TICKS timestamp, int fenceFd) {
Mutex::Autolock autoLock(mLock);
OMX::buffer_id buffer = findBufferID(header);
status_t err = updateGraphicBufferInMeta_l(kPortIndexInput, graphicBuffer, buffer, header);
if (err != OK) {
- CLOG_ERROR(emptyGraphicBuffer, err, FULL_BUFFER((intptr_t)header->pBuffer, header));
+ CLOG_ERROR(emptyGraphicBuffer, err, FULL_BUFFER(
+ (intptr_t)header->pBuffer, header, fenceFd));
return err;
}
header->nOffset = 0;
header->nFilledLen = graphicBuffer == NULL ? 0 : header->nAllocLen;
- return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer);
+ return emptyBuffer_l(header, flags, timestamp, (intptr_t)header->pBuffer, fenceFd);
}
status_t OMXNodeInstance::getExtensionIndex(
@@ -1307,7 +1369,8 @@ void OMXNodeInstance::onMessage(const omx_message &msg) {
mOutputBuffersWithCodec.remove(buffer);
CLOG_BUMPED_BUFFER(
- FBD, WITH_STATS(FULL_BUFFER(msg.u.extended_buffer_data.buffer, buffer)));
+ FBD, WITH_STATS(FULL_BUFFER(
+ msg.u.extended_buffer_data.buffer, buffer, msg.fenceFd)));
unbumpDebugLevel_l(kPortIndexOutput);
}
@@ -1335,7 +1398,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) {
mInputBuffersWithCodec.remove(buffer);
CLOG_BUMPED_BUFFER(
- EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer)));
+ EBD, WITH_STATS(EMPTY_BUFFER(msg.u.buffer_data.buffer, buffer, msg.fenceFd)));
}
if (bufferSource != NULL) {
@@ -1344,7 +1407,7 @@ void OMXNodeInstance::onMessage(const omx_message &msg) {
// Don't dispatch a message back to ACodec, since it doesn't
// know that anyone asked to have the buffer emptied and will
// be very confused.
- bufferSource->codecBufferEmptied(buffer);
+ bufferSource->codecBufferEmptied(buffer, msg.fenceFd);
return;
}
}
@@ -1439,8 +1502,9 @@ OMX_ERRORTYPE OMXNodeInstance::OnEmptyBufferDone(
if (instance->mDying) {
return OMX_ErrorNone;
}
+ int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput);
return instance->owner()->OnEmptyBufferDone(instance->nodeID(),
- instance->findBufferID(pBuffer), pBuffer);
+ instance->findBufferID(pBuffer), pBuffer, fenceFd);
}
// static
@@ -1452,8 +1516,9 @@ OMX_ERRORTYPE OMXNodeInstance::OnFillBufferDone(
if (instance->mDying) {
return OMX_ErrorNone;
}
+ int fenceFd = instance->retrieveFenceFromMeta_l(pBuffer, kPortIndexOutput);
return instance->owner()->OnFillBufferDone(instance->nodeID(),
- instance->findBufferID(pBuffer), pBuffer);
+ instance->findBufferID(pBuffer), pBuffer, fenceFd);
}
void OMXNodeInstance::addActiveBuffer(OMX_U32 portIndex, OMX::buffer_id id) {
diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
index 5f80cbc..9dd26fb 100644
--- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
@@ -517,6 +517,16 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer(
// TODO do we need to support other formats?
srcStride *= 4;
}
+
+ if (nativeMeta.nFenceFd >= 0) {
+ sp<Fence> fence = new Fence(nativeMeta.nFenceFd);
+ nativeMeta.nFenceFd = -1;
+ status_t err = fence->wait(IOMX::kFenceTimeoutMs);
+ if (err != OK) {
+ ALOGE("Timed out waiting on input fence");
+ return NULL;
+ }
+ }
} else {
// TODO: remove this part. Check if anyone uses this.
@@ -526,7 +536,7 @@ const uint8_t *SoftVideoEncoderOMXComponent::extractGraphicBuffer(
}
VideoGrallocMetadata &grallocMeta = *(VideoGrallocMetadata *)(src);
- handle = grallocMeta.hHandle;
+ handle = grallocMeta.pHandle;
// assume HAL_PIXEL_FORMAT_RGBA_8888
// there is no way to get the src stride without the graphic buffer
format = HAL_PIXEL_FORMAT_RGBA_8888;
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index 33bd416..2e68dad 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -18,6 +18,7 @@
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
+#include <private/android_filesystem_config.h>
#include "ServiceUtilities.h"
/* When performing permission checks we do not use permission cache for
@@ -53,6 +54,10 @@ bool recordingAllowed(const String16& opPackageName) {
}
const uid_t uid = IPCThreadState::self()->getCallingUid();
+
+ // To permit command-line native tests
+ if (uid == AID_ROOT) return true;
+
String16 checkedOpPackageName = opPackageName;
// In some cases the calling code has no access to the package it runs under.
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7809eff..dc6710f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2191,6 +2191,10 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l()
ALOGI("HAL output buffer size %u frames, normal sink buffer size %u frames", mFrameCount,
mNormalFrameCount);
+ // Check if we want to throttle the processing to no more than 2x normal rate
+ mThreadThrottle = property_get_bool("af.thread.throttle", true /* default_value */);
+ mHalfBufferMs = mNormalFrameCount * 1000 / (2 * mSampleRate);
+
// mSinkBuffer is the sink buffer. Size is always multiple-of-16 frames.
// Originally this was int16_t[] array, need to remove legacy implications.
free(mSinkBuffer);
@@ -2908,8 +2912,9 @@ bool AudioFlinger::PlaybackThread::threadLoop()
if (!waitingAsyncCallback()) {
// mSleepTimeUs == 0 means we must write to audio hardware
if (mSleepTimeUs == 0) {
+ ssize_t ret = 0;
if (mBytesRemaining) {
- ssize_t ret = threadLoop_write();
+ ret = threadLoop_write();
if (ret < 0) {
mBytesRemaining = 0;
} else {
@@ -2920,11 +2925,11 @@ bool AudioFlinger::PlaybackThread::threadLoop()
(mMixerStatus == MIXER_DRAIN_ALL)) {
threadLoop_drain();
}
- if (mType == MIXER) {
+ if (mType == MIXER && !mStandby) {
// write blocked detection
nsecs_t now = systemTime();
nsecs_t delta = now - mLastWriteTime;
- if (!mStandby && delta > maxPeriod) {
+ if (delta > maxPeriod) {
mNumDelayedWrites++;
if ((now - lastWarning) > kWarningThrottleNs) {
ATRACE_NAME("underrun");
@@ -2933,6 +2938,31 @@ bool AudioFlinger::PlaybackThread::threadLoop()
lastWarning = now;
}
}
+
+ if (mThreadThrottle
+ && mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks)
+ && ret > 0) { // we wrote something
+ // Limit MixerThread data processing to no more than twice the
+ // expected processing rate.
+ //
+ // This helps prevent underruns with NuPlayer and other applications
+ // which may set up buffers that are close to the minimum size, or use
+ // deep buffers, and rely on a double-buffering sleep strategy to fill.
+ //
+ // The throttle smooths out sudden large data drains from the device,
+ // e.g. when it comes out of standby, which often causes problems with
+ // (1) mixer threads without a fast mixer (which has its own warm-up)
+ // (2) minimum buffer sized tracks (even if the track is full,
+ // the app won't fill fast enough to handle the sudden draw).
+
+ const int32_t deltaMs = delta / 1000000;
+ const int32_t throttleMs = mHalfBufferMs - deltaMs;
+ if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
+ usleep(throttleMs * 1000);
+ ALOGD("mixer(%p) throttle: ret(%zd) deltaMs(%d) requires sleep %d ms",
+ this, ret, deltaMs, throttleMs);
+ }
+ }
}
} else {
@@ -4023,6 +4053,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
}
} else {
if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
+ ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
+ track, framesReady, desiredFrames);
track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
}
// clear effect chain input buffer if an active track underruns to avoid sending
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 07c226e..7b4fb14 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -613,6 +613,9 @@ protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
+ bool mThreadThrottle; // throttle the thread processing
+ uint32_t mHalfBufferMs; // half the buffer size in milliseconds
+
void* mSinkBuffer; // frame size aligned sink buffer
// TODO: