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();
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;
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);
private:
enum {
- kWhatCodecNotify,
+ kWhatCodecNotify = 'cdcN',
};
sp<AMessage> mNotify;
sp<NativeWindowWrapper> mNativeWindow;
sp<ACodec> mCodec;
+ sp<ALooper> mCodecLooper;
Vector<sp<ABuffer> > mCSD;
size_t mCSDIndex;
mDrainAudioQueuePending = false;
- onDrainAudioQueue();
-
- postDrainAudioQueue();
+ if (onDrainAudioQueue()) {
+ uint32_t numFramesPlayed;
+ CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed),
+ (status_t)OK);
+
+ 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;
}
}
}
-void NuPlayer::Renderer::postDrainAudioQueue() {
+void NuPlayer::Renderer::postDrainAudioQueue(int64_t delayUs) {
if (mDrainAudioQueuePending || mSyncQueues || mPaused) {
return;
}
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) {
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) {
entry = NULL;
}
- mNumFramesWritten += copy / mAudioSink->frameSize();
+ numBytesAvailableToWrite -= copy;
+ size_t copiedFrames = copy / mAudioSink->frameSize();
+ mNumFramesWritten += copiedFrames;
}
notifyPosition();
+
+ return !mAudioQueue.empty();
}
void NuPlayer::Renderer::postDrainVideoQueue() {
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);
void resume();
enum {
- kWhatEOS,
- kWhatFlushComplete,
- kWhatPosition,
+ kWhatEOS = 'eos ',
+ kWhatFlushComplete = 'fluC',
+ kWhatPosition = 'posi',
};
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 {
int64_t mLastPositionUpdateUs;
- void onDrainAudioQueue();
- void postDrainAudioQueue();
+ bool onDrainAudioQueue();
+ void postDrainAudioQueue(int64_t delayUs = 0);
void onDrainVideoQueue();
void postDrainVideoQueue();
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);
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,
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(
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 =
++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;
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;
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();
}
: mOwner(owner),
mDone(false) {
mThread = new CallbackDispatcherThread(this);
- mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_AUDIO);
+ mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
}
OMX::CallbackDispatcher::~CallbackDispatcher() {
mLooper->start(
false, // runOnCallingThread
false, // canCallJava
- PRIORITY_AUDIO);
+ ANDROID_PRIORITY_FOREGROUND);
}
void SimpleSoftOMXComponent::prepareForDestruction() {