summaryrefslogtreecommitdiffstats
path: root/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp')
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp119
1 files changed, 97 insertions, 22 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 396ead6..eb4e67d 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,36 @@
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
+
+ #Use deep buffer for PCM data with video (it is generally enabled for audio-only)
+ adb shell setprop media.stagefright.audio.deep 1
+
+ #Set size of buffers for pcm audio sink in msec (example: 1000 msec)
+ adb shell setprop media.stagefright.audio.sink 1000
+
+ * 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 */);
+}
+
+static inline int32_t getAudioSinkPcmMsSetting() {
+ return property_get_int32(
+ "media.stagefright.audio.sink", 500 /* 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 +117,7 @@ NuPlayer::Renderer::Renderer(
mCurrentPcmInfo(AUDIO_PCMINFO_INITIALIZER),
mTotalBuffersQueued(0),
mLastAudioBufferDrained(0),
+ mUseAudioCallback(false),
mWakeLock(new AWakeLock()) {
mMediaClock = new MediaClock;
mPlaybackRate = mPlaybackSettings.mSpeed;
@@ -592,8 +623,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
}
void NuPlayer::Renderer::postDrainAudioQueue_l(int64_t delayUs) {
- if (mDrainAudioQueuePending || mSyncQueues || mPaused
- || offloadingAudio()) {
+ if (mDrainAudioQueuePending || mSyncQueues || mUseAudioCallback) {
return;
}
@@ -642,12 +672,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 +691,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) {
return 0;
}
@@ -667,13 +699,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 +713,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 +746,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;
}
@@ -749,6 +799,7 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
}
#endif
+ uint32_t prevFramesWritten = mNumFramesWritten;
while (!mAudioQueue.empty()) {
QueueEntry *entry = &*mAudioQueue.begin();
@@ -778,7 +829,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);
}
@@ -846,7 +898,13 @@ bool NuPlayer::Renderer::onDrainAudioQueue() {
}
mMediaClock->updateMaxTimeMedia(maxTimeMedia);
- return !mAudioQueue.empty();
+ // calculate whether we need to reschedule another write.
+ bool reschedule = !mAudioQueue.empty()
+ && (!mPaused
+ || prevFramesWritten != mNumFramesWritten); // permit pause to fill buffers
+ //ALOGD("reschedule:%d empty:%d mPaused:%d prevFramesWritten:%u mNumFramesWritten:%u",
+ // reschedule, mAudioQueue.empty(), mPaused, prevFramesWritten, mNumFramesWritten);
+ return reschedule;
}
int64_t NuPlayer::Renderer::getDurationUsIfPlayedAtSampleRate(uint32_t numFrames) {
@@ -1230,9 +1288,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;
@@ -1240,7 +1297,9 @@ void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
if (offloadingAudio()) {
mAudioSink->pause();
mAudioSink->flush();
- mAudioSink->start();
+ if (!mPaused) {
+ mAudioSink->start();
+ }
} else {
mAudioSink->pause();
mAudioSink->flush();
@@ -1345,7 +1404,7 @@ void NuPlayer::Renderer::onPause() {
{
Mutex::Autolock autoLock(mLock);
- ++mAudioDrainGeneration;
+ // we do not increment audio drain generation so that we fill audio buffer during pause.
++mVideoDrainGeneration;
prepareForMediaRenderingStart_l();
mPaused = true;
@@ -1590,12 +1649,13 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
offloadFlags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
audioSinkChanged = true;
mAudioSink->close();
+
err = mAudioSink->open(
sampleRate,
numChannels,
(audio_channel_mask_t)channelMask,
audioFormat,
- 8 /* bufferCount */,
+ 0 /* bufferCount - unused */,
&NuPlayer::Renderer::AudioSinkCallback,
this,
(audio_output_flags_t)offloadFlags,
@@ -1613,7 +1673,9 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
// before reaching the hardware.
// TODO
mCurrentOffloadInfo = offloadInfo;
- err = mAudioSink->start();
+ if (!mPaused) { // for preview mode, don't start if paused
+ err = mAudioSink->start();
+ }
ALOGV_IF(err == OK, "openAudioSink: offload succeeded");
}
if (err != OK) {
@@ -1623,6 +1685,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,17 +1709,27 @@ 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();
+
+ // Compute the desired buffer size.
+ // For callback mode, the amount of time before wakeup is about half the buffer size.
+ const uint32_t frameCount =
+ (unsigned long long)sampleRate * getAudioSinkPcmMsSetting() / 1000;
+
status_t err = mAudioSink->open(
sampleRate,
numChannels,
(audio_channel_mask_t)channelMask,
AUDIO_FORMAT_PCM_16_BIT,
- 8 /* bufferCount */,
- NULL,
- NULL,
+ 0 /* bufferCount - unused */,
+ mUseAudioCallback ? &NuPlayer::Renderer::AudioSinkCallback : NULL,
+ mUseAudioCallback ? this : NULL,
(audio_output_flags_t)pcmFlags,
NULL,
- true /* doNotReconnect */);
+ true /* doNotReconnect */,
+ frameCount);
if (err == OK) {
err = mAudioSink->setPlaybackRate(mPlaybackSettings);
}
@@ -1666,7 +1739,9 @@ status_t NuPlayer::Renderer::onOpenAudioSink(
return err;
}
mCurrentPcmInfo = info;
- mAudioSink->start();
+ if (!mPaused) { // for preview mode, don't start if paused
+ mAudioSink->start();
+ }
}
if (audioSinkChanged) {
onAudioSinkChanged();