Merge "Various improvements to nuplayer playback"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9e7a6fc149
@@ -299,7 +299,12 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
|
||||
sampleRate, numChannels);
|
||||
|
||||
mAudioSink->close();
|
||||
CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
|
||||
CHECK_EQ(mAudioSink->open(
|
||||
sampleRate,
|
||||
numChannels,
|
||||
AUDIO_FORMAT_PCM_16_BIT,
|
||||
8 /* bufferCount */),
|
||||
(status_t)OK);
|
||||
mAudioSink->start();
|
||||
|
||||
mRenderer->signalAudioSinkChanged();
|
||||
|
||||
@@ -70,19 +70,19 @@ private:
|
||||
struct StreamingSource;
|
||||
|
||||
enum {
|
||||
kWhatSetDataSource,
|
||||
kWhatSetVideoNativeWindow,
|
||||
kWhatSetAudioSink,
|
||||
kWhatMoreDataQueued,
|
||||
kWhatStart,
|
||||
kWhatScanSources,
|
||||
kWhatVideoNotify,
|
||||
kWhatAudioNotify,
|
||||
kWhatRendererNotify,
|
||||
kWhatReset,
|
||||
kWhatSeek,
|
||||
kWhatPause,
|
||||
kWhatResume,
|
||||
kWhatSetDataSource = '=DaS',
|
||||
kWhatSetVideoNativeWindow = '=NaW',
|
||||
kWhatSetAudioSink = '=AuS',
|
||||
kWhatMoreDataQueued = 'more',
|
||||
kWhatStart = 'strt',
|
||||
kWhatScanSources = 'scan',
|
||||
kWhatVideoNotify = 'vidN',
|
||||
kWhatAudioNotify = 'audN',
|
||||
kWhatRendererNotify = 'renN',
|
||||
kWhatReset = 'rset',
|
||||
kWhatSeek = 'seek',
|
||||
kWhatPause = 'paus',
|
||||
kWhatResume = 'rsme',
|
||||
};
|
||||
|
||||
wp<NuPlayerDriver> mDriver;
|
||||
|
||||
@@ -59,8 +59,21 @@ void NuPlayer::Decoder::configure(const sp<MetaData> &meta) {
|
||||
format->setObject("native-window", mNativeWindow);
|
||||
}
|
||||
|
||||
// Current video decoders do not return from OMX_FillThisBuffer
|
||||
// quickly, violating the OpenMAX specs, until that is remedied
|
||||
// we need to invest in an extra looper to free the main event
|
||||
// queue.
|
||||
bool needDedicatedLooper = !strncasecmp(mime, "video/", 6);
|
||||
|
||||
mCodec = new ACodec;
|
||||
looper()->registerHandler(mCodec);
|
||||
|
||||
if (needDedicatedLooper && mCodecLooper == NULL) {
|
||||
mCodecLooper = new ALooper;
|
||||
mCodecLooper->setName("NuPlayerDecoder");
|
||||
mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
|
||||
}
|
||||
|
||||
(needDedicatedLooper ? mCodecLooper : looper())->registerHandler(mCodec);
|
||||
|
||||
mCodec->setNotificationMessage(notifyMsg);
|
||||
mCodec->initiateSetup(format);
|
||||
|
||||
@@ -43,13 +43,14 @@ protected:
|
||||
|
||||
private:
|
||||
enum {
|
||||
kWhatCodecNotify,
|
||||
kWhatCodecNotify = 'cdcN',
|
||||
};
|
||||
|
||||
sp<AMessage> mNotify;
|
||||
sp<NativeWindowWrapper> mNativeWindow;
|
||||
|
||||
sp<ACodec> mCodec;
|
||||
sp<ALooper> mCodecLooper;
|
||||
|
||||
Vector<sp<ABuffer> > mCSD;
|
||||
size_t mCSDIndex;
|
||||
|
||||
@@ -118,9 +118,24 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
|
||||
|
||||
mDrainAudioQueuePending = false;
|
||||
|
||||
onDrainAudioQueue();
|
||||
if (onDrainAudioQueue()) {
|
||||
uint32_t numFramesPlayed;
|
||||
CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed),
|
||||
(status_t)OK);
|
||||
|
||||
postDrainAudioQueue();
|
||||
uint32_t numFramesPendingPlayout =
|
||||
mNumFramesWritten - numFramesPlayed;
|
||||
|
||||
// This is how long the audio sink will have data to
|
||||
// play back.
|
||||
int64_t delayUs =
|
||||
mAudioSink->msecsPerFrame()
|
||||
* numFramesPendingPlayout * 1000ll;
|
||||
|
||||
// Let's give it more data after about half that time
|
||||
// has elapsed.
|
||||
postDrainAudioQueue(delayUs / 2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -182,7 +197,7 @@ void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
|
||||
}
|
||||
}
|
||||
|
||||
void NuPlayer::Renderer::postDrainAudioQueue() {
|
||||
void NuPlayer::Renderer::postDrainAudioQueue(int64_t delayUs) {
|
||||
if (mDrainAudioQueuePending || mSyncQueues || mPaused) {
|
||||
return;
|
||||
}
|
||||
@@ -194,19 +209,33 @@ void NuPlayer::Renderer::postDrainAudioQueue() {
|
||||
mDrainAudioQueuePending = true;
|
||||
sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
|
||||
msg->setInt32("generation", mAudioQueueGeneration);
|
||||
msg->post();
|
||||
msg->post(delayUs);
|
||||
}
|
||||
|
||||
void NuPlayer::Renderer::signalAudioSinkChanged() {
|
||||
(new AMessage(kWhatAudioSinkChanged, id()))->post();
|
||||
}
|
||||
|
||||
void NuPlayer::Renderer::onDrainAudioQueue() {
|
||||
for (;;) {
|
||||
if (mAudioQueue.empty()) {
|
||||
break;
|
||||
}
|
||||
bool NuPlayer::Renderer::onDrainAudioQueue() {
|
||||
uint32_t numFramesPlayed;
|
||||
CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
|
||||
|
||||
ssize_t numFramesAvailableToWrite =
|
||||
mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
|
||||
|
||||
#if 0
|
||||
if (numFramesAvailableToWrite == mAudioSink->frameCount()) {
|
||||
LOGI("audio sink underrun");
|
||||
} else {
|
||||
LOGV("audio queue has %d frames left to play",
|
||||
mAudioSink->frameCount() - numFramesAvailableToWrite);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t numBytesAvailableToWrite =
|
||||
numFramesAvailableToWrite * mAudioSink->frameSize();
|
||||
|
||||
while (numBytesAvailableToWrite > 0 && !mAudioQueue.empty()) {
|
||||
QueueEntry *entry = &*mAudioQueue.begin();
|
||||
|
||||
if (entry->mBuffer == NULL) {
|
||||
@@ -216,20 +245,7 @@ void NuPlayer::Renderer::onDrainAudioQueue() {
|
||||
|
||||
mAudioQueue.erase(mAudioQueue.begin());
|
||||
entry = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t numFramesPlayed;
|
||||
CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
|
||||
|
||||
ssize_t numFramesAvailableToWrite =
|
||||
mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
|
||||
|
||||
size_t numBytesAvailableToWrite =
|
||||
numFramesAvailableToWrite * mAudioSink->frameSize();
|
||||
|
||||
if (numBytesAvailableToWrite == 0) {
|
||||
break;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entry->mOffset == 0) {
|
||||
@@ -274,10 +290,14 @@ void NuPlayer::Renderer::onDrainAudioQueue() {
|
||||
entry = NULL;
|
||||
}
|
||||
|
||||
mNumFramesWritten += copy / mAudioSink->frameSize();
|
||||
numBytesAvailableToWrite -= copy;
|
||||
size_t copiedFrames = copy / mAudioSink->frameSize();
|
||||
mNumFramesWritten += copiedFrames;
|
||||
}
|
||||
|
||||
notifyPosition();
|
||||
|
||||
return !mAudioQueue.empty();
|
||||
}
|
||||
|
||||
void NuPlayer::Renderer::postDrainVideoQueue() {
|
||||
@@ -344,7 +364,14 @@ void NuPlayer::Renderer::onDrainVideoQueue() {
|
||||
int64_t mediaTimeUs;
|
||||
CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
|
||||
|
||||
LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
|
||||
int64_t realTimeUs = mediaTimeUs - mAnchorTimeMediaUs + mAnchorTimeRealUs;
|
||||
int64_t lateByUs = ALooper::GetNowUs() - realTimeUs;
|
||||
|
||||
if (lateByUs > 40000) {
|
||||
LOGI("video late by %lld us (%.2f secs)", lateByUs, lateByUs / 1E6);
|
||||
} else {
|
||||
LOGV("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
|
||||
}
|
||||
#endif
|
||||
|
||||
entry->mNotifyConsumed->setInt32("render", true);
|
||||
|
||||
@@ -45,9 +45,9 @@ struct NuPlayer::Renderer : public AHandler {
|
||||
void resume();
|
||||
|
||||
enum {
|
||||
kWhatEOS,
|
||||
kWhatFlushComplete,
|
||||
kWhatPosition,
|
||||
kWhatEOS = 'eos ',
|
||||
kWhatFlushComplete = 'fluC',
|
||||
kWhatPosition = 'posi',
|
||||
};
|
||||
|
||||
protected:
|
||||
@@ -57,14 +57,14 @@ protected:
|
||||
|
||||
private:
|
||||
enum {
|
||||
kWhatDrainAudioQueue,
|
||||
kWhatDrainVideoQueue,
|
||||
kWhatQueueBuffer,
|
||||
kWhatQueueEOS,
|
||||
kWhatFlush,
|
||||
kWhatAudioSinkChanged,
|
||||
kWhatPause,
|
||||
kWhatResume,
|
||||
kWhatDrainAudioQueue = 'draA',
|
||||
kWhatDrainVideoQueue = 'draV',
|
||||
kWhatQueueBuffer = 'queB',
|
||||
kWhatQueueEOS = 'qEOS',
|
||||
kWhatFlush = 'flus',
|
||||
kWhatAudioSinkChanged = 'auSC',
|
||||
kWhatPause = 'paus',
|
||||
kWhatResume = 'resm',
|
||||
};
|
||||
|
||||
struct QueueEntry {
|
||||
@@ -102,8 +102,8 @@ private:
|
||||
|
||||
int64_t mLastPositionUpdateUs;
|
||||
|
||||
void onDrainAudioQueue();
|
||||
void postDrainAudioQueue();
|
||||
bool onDrainAudioQueue();
|
||||
void postDrainAudioQueue(int64_t delayUs = 0);
|
||||
|
||||
void onDrainVideoQueue();
|
||||
void postDrainVideoQueue();
|
||||
|
||||
@@ -52,7 +52,7 @@ bool NuPlayer::StreamingSource::feedMoreTSData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < 10; ++i) {
|
||||
for (int32_t i = 0; i < 50; ++i) {
|
||||
char buffer[188];
|
||||
sp<AMessage> extra;
|
||||
ssize_t n = mStreamListener->read(buffer, sizeof(buffer), &extra);
|
||||
|
||||
@@ -1377,8 +1377,13 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
|
||||
memcpy(info->mData->data(), buffer->data(), buffer->size());
|
||||
}
|
||||
|
||||
LOGV("[%s] calling emptyBuffer %p",
|
||||
mCodec->mComponentName.c_str(), bufferID);
|
||||
if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
|
||||
LOGV("[%s] calling emptyBuffer %p w/ codec specific data",
|
||||
mCodec->mComponentName.c_str(), bufferID);
|
||||
} else {
|
||||
LOGV("[%s] calling emptyBuffer %p w/ time %lld us",
|
||||
mCodec->mComponentName.c_str(), bufferID, timeUs);
|
||||
}
|
||||
|
||||
CHECK_EQ(mCodec->mOMX->emptyBuffer(
|
||||
mCodec->mNode,
|
||||
@@ -1396,7 +1401,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
|
||||
LOGV("[%s] Signalling EOS on the input port",
|
||||
mCodec->mComponentName.c_str());
|
||||
|
||||
LOGV("[%s] calling emptyBuffer %p",
|
||||
LOGV("[%s] calling emptyBuffer %p signalling EOS",
|
||||
mCodec->mComponentName.c_str(), bufferID);
|
||||
|
||||
CHECK_EQ(mCodec->mOMX->emptyBuffer(
|
||||
@@ -1457,8 +1462,8 @@ bool ACodec::BaseState::onOMXFillBufferDone(
|
||||
int64_t timeUs,
|
||||
void *platformPrivate,
|
||||
void *dataPtr) {
|
||||
LOGV("[%s] onOMXFillBufferDone %p",
|
||||
mCodec->mComponentName.c_str(), bufferID);
|
||||
LOGV("[%s] onOMXFillBufferDone %p time %lld us",
|
||||
mCodec->mComponentName.c_str(), bufferID, timeUs);
|
||||
|
||||
ssize_t index;
|
||||
BufferInfo *info =
|
||||
@@ -1686,7 +1691,11 @@ void ACodec::UninitializedState::onSetup(
|
||||
++matchIndex) {
|
||||
componentName = matchingCodecs.itemAt(matchIndex).string();
|
||||
|
||||
pid_t tid = androidGetTid();
|
||||
int prevPriority = androidGetThreadPriority(tid);
|
||||
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
|
||||
status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
|
||||
androidSetThreadPriority(tid, prevPriority);
|
||||
|
||||
if (err == OK) {
|
||||
break;
|
||||
|
||||
@@ -74,10 +74,32 @@ bool logMessageHandler(
|
||||
return false;
|
||||
}
|
||||
|
||||
struct AutoPrioritySaver {
|
||||
AutoPrioritySaver()
|
||||
: mTID(androidGetTid()),
|
||||
mPrevPriority(androidGetThreadPriority(mTID)) {
|
||||
androidSetThreadPriority(mTID, ANDROID_PRIORITY_NORMAL);
|
||||
}
|
||||
|
||||
~AutoPrioritySaver() {
|
||||
androidSetThreadPriority(mTID, mPrevPriority);
|
||||
}
|
||||
|
||||
private:
|
||||
pid_t mTID;
|
||||
int mPrevPriority;
|
||||
|
||||
DISALLOW_EVIL_CONSTRUCTORS(AutoPrioritySaver);
|
||||
};
|
||||
|
||||
static void InitializeNetworkThreadIfNecessary() {
|
||||
Mutex::Autolock autoLock(gNetworkThreadLock);
|
||||
|
||||
if (gNetworkThread == NULL) {
|
||||
// Make sure any threads spawned by the chromium framework are
|
||||
// running at normal priority instead of inheriting this thread's.
|
||||
AutoPrioritySaver saver;
|
||||
|
||||
gNetworkThread = new base::Thread("network");
|
||||
base::Thread::Options options;
|
||||
options.message_loop_type = MessageLoop::TYPE_IO;
|
||||
|
||||
@@ -385,6 +385,15 @@ AString AMessage::debugString(int32_t indent) const {
|
||||
item.u.refValue)->debugString(
|
||||
indent + strlen(item.mName) + 14).c_str());
|
||||
break;
|
||||
case kTypeRect:
|
||||
tmp = StringPrintf(
|
||||
"Rect %s(%d, %d, %d, %d)",
|
||||
item.mName,
|
||||
item.u.rectValue.mLeft,
|
||||
item.u.rectValue.mTop,
|
||||
item.u.rectValue.mRight,
|
||||
item.u.rectValue.mBottom);
|
||||
break;
|
||||
default:
|
||||
TRESPASS();
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ OMX::CallbackDispatcher::CallbackDispatcher(OMXNodeInstance *owner)
|
||||
: mOwner(owner),
|
||||
mDone(false) {
|
||||
mThread = new CallbackDispatcherThread(this);
|
||||
mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_AUDIO);
|
||||
mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
|
||||
}
|
||||
|
||||
OMX::CallbackDispatcher::~CallbackDispatcher() {
|
||||
|
||||
@@ -42,7 +42,7 @@ SimpleSoftOMXComponent::SimpleSoftOMXComponent(
|
||||
mLooper->start(
|
||||
false, // runOnCallingThread
|
||||
false, // canCallJava
|
||||
PRIORITY_AUDIO);
|
||||
ANDROID_PRIORITY_FOREGROUND);
|
||||
}
|
||||
|
||||
void SimpleSoftOMXComponent::prepareForDestruction() {
|
||||
|
||||
Reference in New Issue
Block a user