Merge "Don't overwrite user agent header if app specified one" into mnc-dev
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 @@
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 @@
}
}
+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 @@
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 @@
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::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 @@
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 @@
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 @@
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 @@
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 @@
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/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 7c40121..02c1c38 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1744,12 +1744,14 @@
void MediaPlayerService::AudioOutput::stop()
{
ALOGV("stop");
+ mBytesWritten = 0;
if (mTrack != 0) mTrack->stop();
}
void MediaPlayerService::AudioOutput::flush()
{
ALOGV("flush");
+ mBytesWritten = 0;
if (mTrack != 0) mTrack->flush();
}
@@ -1851,20 +1853,23 @@
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);
@@ -1877,11 +1882,15 @@
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 @@
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::postDrainAudioQueue_l(int64_t delayUs) {
if (mDrainAudioQueuePending || mSyncQueues || mPaused
- || offloadingAudio()) {
+ || mUseAudioCallback) {
return;
}
@@ -642,12 +662,14 @@
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::fillAudioBuffer(void *buffer, size_t size) {
Mutex::Autolock autoLock(mLock);
- if (!offloadingAudio() || mPaused) {
+ if (!mUseAudioCallback || mPaused) {
return 0;
}
@@ -667,13 +689,13 @@
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 @@
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 @@
mMediaClock->updateAnchor(nowMediaUs, nowUs, INT64_MAX);
}
- if (hasEOS) {
- (new AMessage(kWhatStopAudioSink, this))->post();
+ // 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 @@
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 @@
++mAudioDrainGeneration;
prepareForMediaRenderingStart_l();
- if (offloadingAudio()) {
- clearAudioFirstAnchorTime_l();
- }
+ // the frame count will be reset after flush.
+ clearAudioFirstAnchorTime_l();
}
mDrainAudioQueuePending = false;
@@ -1590,6 +1630,7 @@
offloadFlags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
audioSinkChanged = true;
mAudioSink->close();
+
err = mAudioSink->open(
sampleRate,
numChannels,
@@ -1623,6 +1664,7 @@
mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER;
ALOGV("openAudioSink: offload failed");
}
+ mUseAudioCallback = true; // offload mode transfers data through callback
}
}
if (!offloadOnly && !offloadingAudio()) {
@@ -1646,14 +1688,17 @@
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 @@
int32_t mTotalBuffersQueued;
int32_t mLastAudioBufferDrained;
+ bool mUseAudioCallback;
sp<AWakeLock> mWakeLock;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index a770746..3a98e8c 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1285,7 +1285,7 @@
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 =
@@ -4945,7 +4945,7 @@
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;
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 @@
// 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/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 @@
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/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index be91510..d30bba5 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -275,10 +275,10 @@
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
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index 4ba4aeb..9e399f9 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -812,7 +812,7 @@
&& 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);
@@ -1113,7 +1113,7 @@
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 {
diff --git a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
index cd1ac36..9dd26fb 100644
--- a/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoEncoderOMXComponent.cpp
@@ -536,7 +536,7 @@
}
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/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 @@
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 @@
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 @@
(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 @@
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 @@
}
} 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 @@
// 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: