Deliver non-realtime OMX messages in a timely manner
Allow non-realtime messages to always be delivered even if the
end-of-stream is never reached. Start using a timeout to process
all messages on a periodic basis after the first non-realtime
message is encountered. This guarantees that non-realtime
messages are delivered in a reasonable amount of time.
This only affects tunnel mode decoders, since they are the only
decoders that produce frame-rendered messages, and frame-rendered
messages are the only non-realtime messages.
Note that this introduces periodic wakeups even when the decoder
is not actively rendering. This should not be problematic from a
performance perspective since a non-active decoder means the
device is likely underutilized.
Bug: 240622361
Fixes: 240622361
Test: atest android.media.decoder.cts.DecoderTest#testTunneledAudioUnderrunHevc
Change-Id: I0b9d4bcd56cc64fc37f52d295111e4e3eb6a157f
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index bebd516..c82a303 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -316,7 +316,7 @@
// that a new message is available on the queue. Otherwise, the message stays on the queue, but
// the listener is not notified of it. It will process this message when a subsequent message
// is posted with |realTime| set to true.
- void post(const omx_message &msg, bool realTime = true);
+ void post(const omx_message &msg, bool realTime);
bool loop();
@@ -325,18 +325,15 @@
private:
enum {
- // This is used for frame_rendered message batching, which will eventually end up in a
- // single AMessage in MediaCodec when it is signaled to the app. AMessage can contain
- // up-to 64 key-value pairs, and each frame_rendered message uses 2 keys, so the max
- // value for this would be 32. Nonetheless, limit this to 12 to which gives at least 10
- // mseconds of batching at 120Hz.
- kMaxQueueSize = 12,
+ // Don't delay non-realtime messages longer than 200ms
+ kMaxBatchedDelayNs = 200 * 1000 * 1000,
};
Mutex mLock;
sp<OMXNodeInstance> const mOwner;
bool mDone;
+ bool mHasBatchedMessages;
Condition mQueueChanged;
std::list<omx_message> mQueue;
@@ -350,7 +347,8 @@
OMXNodeInstance::CallbackDispatcher::CallbackDispatcher(const sp<OMXNodeInstance> &owner)
: mOwner(owner),
- mDone(false) {
+ mDone(false),
+ mHasBatchedMessages(false) {
mThread = new CallbackDispatcherThread(this);
mThread->run("OMXCallbackDisp", ANDROID_PRIORITY_FOREGROUND);
}
@@ -358,7 +356,6 @@
OMXNodeInstance::CallbackDispatcher::~CallbackDispatcher() {
{
Mutex::Autolock autoLock(mLock);
-
mDone = true;
mQueueChanged.signal();
}
@@ -377,8 +374,11 @@
Mutex::Autolock autoLock(mLock);
mQueue.push_back(msg);
- if (realTime || mQueue.size() >= kMaxQueueSize) {
+ if (realTime) {
mQueueChanged.signal();
+ } else if (!mHasBatchedMessages) {
+ mHasBatchedMessages = true;
+ mQueueChanged.signal(); // The first non-realtime message is not batched.
}
}
@@ -393,11 +393,16 @@
bool OMXNodeInstance::CallbackDispatcher::loop() {
for (;;) {
std::list<omx_message> messages;
+ std::list<long long> messageTimestamps;
{
Mutex::Autolock autoLock(mLock);
while (!mDone && mQueue.empty()) {
- mQueueChanged.wait(mLock);
+ if (mHasBatchedMessages) {
+ mQueueChanged.waitRelative(mLock, kMaxBatchedDelayNs);
+ } else {
+ mQueueChanged.wait(mLock);
+ }
}
if (mDone) {
@@ -2447,7 +2452,7 @@
msg.type = omx_message::EMPTY_BUFFER_DONE;
msg.fenceFd = fenceFd;
msg.u.buffer_data.buffer = instance->findBufferID(pBuffer);
- instance->mDispatcher->post(msg);
+ instance->mDispatcher->post(msg, true /* realTime */);
return OMX_ErrorNone;
}
@@ -2475,7 +2480,7 @@
msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
msg.u.extended_buffer_data.flags = pBuffer->nFlags;
msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
- instance->mDispatcher->post(msg);
+ instance->mDispatcher->post(msg, true /* realTime */);
return OMX_ErrorNone;
}