Loading include/media/stagefright/ACodec.h +1 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ private: int32_t mChannelMask; unsigned mDequeueCounter; bool mStoreMetaDataInOutputBuffers; bool mLegacyAdaptiveExperiment; int32_t mMetaDataBuffersToSubmit; size_t mNumUndequeuedBuffers; Loading include/media/stagefright/foundation/ADebug.h +20 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,26 @@ struct ADebug { // remove redundant segments of a codec name, and return a newly allocated // string suitable for debugging static char *GetDebugName(const char *name); inline static bool isExperimentEnabled( const char *name __unused /* nonnull */, bool allow __unused = true) { #ifdef ENABLE_STAGEFRIGHT_EXPERIMENTS if (!strcmp(name, "legacy-adaptive")) { return getExperimentFlag(allow, name, 2, 1); // every other day } else if (!strcmp(name, "legacy-setsurface")) { return getExperimentFlag(allow, name, 3, 1); // every third day } else { ALOGE("unknown experiment '%s' (disabled)", name); } #endif return false; } private: // pass in allow, so we can print in the log if the experiment is disabled static bool getExperimentFlag( bool allow, const char *name, uint64_t modulo, uint64_t limit, uint64_t plus = 0, uint64_t timeDivisor = 24 * 60 * 60 /* 1 day */); }; } // namespace android Loading media/libstagefright/ACodec.cpp +97 −17 Original line number Diff line number Diff line Loading @@ -422,6 +422,7 @@ ACodec::ACodec() mChannelMask(0), mDequeueCounter(0), mStoreMetaDataInOutputBuffers(false), mLegacyAdaptiveExperiment(false), mMetaDataBuffersToSubmit(0), mRepeatFrameDelayUs(-1ll), mMaxPtsGapUs(-1ll), Loading Loading @@ -610,12 +611,16 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { return err; } // need to enable allocation when attaching surface->getIGraphicBufferProducer()->allowAllocation(true); // for meta data mode, we move dequeud buffers to the new surface. // for non-meta mode, we must move all registered buffers for (size_t i = 0; i < buffers.size(); ++i) { const BufferInfo &info = buffers[i]; // skip undequeued buffers for meta data mode if (mStoreMetaDataInOutputBuffers && !mLegacyAdaptiveExperiment && info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGV("skipping buffer %p", info.mGraphicBuffer->getNativeBuffer()); continue; Loading @@ -632,7 +637,7 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { } // cancel undequeued buffers to new surface if (!mStoreMetaDataInOutputBuffers) { if (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment) { for (size_t i = 0; i < buffers.size(); ++i) { const BufferInfo &info = buffers[i]; if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { Loading Loading @@ -967,6 +972,44 @@ status_t ACodec::allocateOutputMetaDataBuffers() { return err; mNumUndequeuedBuffers = minUndequeuedBuffers; if (mLegacyAdaptiveExperiment) { // preallocate buffers static_cast<Surface *>(mNativeWindow.get()) ->getIGraphicBufferProducer()->allowAllocation(true); ALOGV("[%s] Allocating %u buffers from a native window of size %u on " "output port", mComponentName.c_str(), bufferCount, bufferSize); // Dequeue buffers then cancel them all for (OMX_U32 i = 0; i < bufferCount; i++) { ANativeWindowBuffer *buf; err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; } sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); } for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); status_t error = cancelBufferToNativeWindow(info); if (err == OK) { err = error; } } mBuffers[kPortIndexOutput].clear(); static_cast<Surface*>(mNativeWindow.get()) ->getIGraphicBufferProducer()->allowAllocation(false); } ALOGV("[%s] Allocating %u meta buffers on output port", mComponentName.c_str(), bufferCount); Loading Loading @@ -1046,26 +1089,57 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { return NULL; } do { if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { ALOGE("dequeueBuffer failed."); return NULL; } BufferInfo *oldest = NULL; bool stale = false; for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); if (info->mGraphicBuffer != NULL && info->mGraphicBuffer->handle == buf->handle) { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_NATIVE_WINDOW); // Since consumers can attach buffers to BufferQueues, it is possible // that a known yet stale buffer can return from a surface that we // once used. We can simply ignore this as we have already dequeued // this buffer properly. NOTE: this does not eliminate all cases, // e.g. it is possible that we have queued the valid buffer to the // NW, and a stale copy of the same buffer gets dequeued - which will // be treated as the valid buffer by ACodec. if (info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGI("dequeued stale buffer %p. discarding", buf); stale = true; break; } ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer()); info->mStatus = BufferInfo::OWNED_BY_US; return info; } } // It is also possible to receive a previously unregistered buffer // in non-meta mode. These should be treated as stale buffers. The // same is possible in meta mode, in which case, it will be treated // as a normal buffer, which is not desirable. // TODO: fix this. if (!stale && (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment)) { ALOGI("dequeued unrecognized (stale) buffer %p. discarding", buf); stale = true; } if (stale) { // TODO: detach stale buffer, but there is no API yet to do it. buf = NULL; } } while (buf == NULL); // get oldest undequeued buffer BufferInfo *oldest = NULL; for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW && (oldest == NULL || // avoid potential issues from counter rolling over Loading Loading @@ -1384,6 +1458,7 @@ status_t ACodec::configureCodec( bool haveNativeWindow = msg->findObject("native-window", &obj) && obj != NULL && video && !encoder; mStoreMetaDataInOutputBuffers = false; mLegacyAdaptiveExperiment = false; if (video && !encoder) { inputFormat->setInt32("adaptive-playback", false); Loading Loading @@ -1521,6 +1596,9 @@ status_t ACodec::configureCodec( ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); mStoreMetaDataInOutputBuffers = true; mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled( "legacy-adaptive", !msg->contains("no-experiments")); inputFormat->setInt32("adaptive-playback", true); } Loading Loading @@ -4135,7 +4213,9 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { sp<RefBase> obj; CHECK(msg->findObject("surface", &obj)); status_t err = mCodec->handleSetSurface(static_cast<Surface *>(obj.get())); status_t err = ADebug::isExperimentEnabled("legacy-setsurface") ? BAD_VALUE : mCodec->handleSetSurface(static_cast<Surface *>(obj.get())); sp<AMessage> response = new AMessage; response->setInt32("err", err); Loading media/libstagefright/Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -124,7 +124,7 @@ LOCAL_SHARED_LIBRARIES += \ libdl \ libRScpp \ LOCAL_CFLAGS += -Wno-multichar -Werror -Wall LOCAL_CFLAGS += -Wno-multichar -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS LOCAL_CLANG := true LOCAL_MODULE:= libstagefright Loading media/libstagefright/foundation/ADebug.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <ctype.h> #define LOG_TAG "ADebug" #include <cutils/atomic.h> #include <utils/Log.h> #include <utils/misc.h> Loading Loading @@ -113,5 +114,43 @@ char *ADebug::GetDebugName(const char *name) { return debugName; } //static bool ADebug::getExperimentFlag( bool allow, const char *name, uint64_t modulo, uint64_t limit, uint64_t plus, uint64_t timeDivisor) { static volatile int32_t haveSerial = 0; static uint64_t serialNum; if (!android_atomic_acquire_load(&haveSerial)) { // calculate initial counter value based on serial number static char serial[PROPERTY_VALUE_MAX]; property_get("ro.serialno", serial, "0"); uint64_t num = 0; // it is okay for this number to overflow for (size_t i = 0; i < NELEM(serial) && serial[i] != '\0'; ++i) { const char &c = serial[i]; // try to use most letters of serialno if (isdigit(c)) { num = num * 10 + (c - '0'); } else if (islower(c)) { num = num * 26 + (c - 'a'); } else if (isupper(c)) { num = num * 26 + (c - 'A'); } else { num = num * 256 + c; } } ALOGI("got serial"); serialNum = num; android_atomic_release_store(1, &haveSerial); } ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL)); // MINOR: use modulo for counter and time, so that their sum does not // roll over, and mess up the correlation between related experiments. // e.g. keep (a mod 2N) = 0 impl (a mod N) = 0 time_t counter = (time(NULL) / timeDivisor) % modulo + plus + serialNum % modulo; bool enable = allow && (counter % modulo < limit); ALOGI("experiment '%s': %s", name, enable ? "ENABLED" : "disabled"); return enable; } } // namespace android Loading
include/media/stagefright/ACodec.h +1 −0 Original line number Diff line number Diff line Loading @@ -216,6 +216,7 @@ private: int32_t mChannelMask; unsigned mDequeueCounter; bool mStoreMetaDataInOutputBuffers; bool mLegacyAdaptiveExperiment; int32_t mMetaDataBuffersToSubmit; size_t mNumUndequeuedBuffers; Loading
include/media/stagefright/foundation/ADebug.h +20 −0 Original line number Diff line number Diff line Loading @@ -108,6 +108,26 @@ struct ADebug { // remove redundant segments of a codec name, and return a newly allocated // string suitable for debugging static char *GetDebugName(const char *name); inline static bool isExperimentEnabled( const char *name __unused /* nonnull */, bool allow __unused = true) { #ifdef ENABLE_STAGEFRIGHT_EXPERIMENTS if (!strcmp(name, "legacy-adaptive")) { return getExperimentFlag(allow, name, 2, 1); // every other day } else if (!strcmp(name, "legacy-setsurface")) { return getExperimentFlag(allow, name, 3, 1); // every third day } else { ALOGE("unknown experiment '%s' (disabled)", name); } #endif return false; } private: // pass in allow, so we can print in the log if the experiment is disabled static bool getExperimentFlag( bool allow, const char *name, uint64_t modulo, uint64_t limit, uint64_t plus = 0, uint64_t timeDivisor = 24 * 60 * 60 /* 1 day */); }; } // namespace android Loading
media/libstagefright/ACodec.cpp +97 −17 Original line number Diff line number Diff line Loading @@ -422,6 +422,7 @@ ACodec::ACodec() mChannelMask(0), mDequeueCounter(0), mStoreMetaDataInOutputBuffers(false), mLegacyAdaptiveExperiment(false), mMetaDataBuffersToSubmit(0), mRepeatFrameDelayUs(-1ll), mMaxPtsGapUs(-1ll), Loading Loading @@ -610,12 +611,16 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { return err; } // need to enable allocation when attaching surface->getIGraphicBufferProducer()->allowAllocation(true); // for meta data mode, we move dequeud buffers to the new surface. // for non-meta mode, we must move all registered buffers for (size_t i = 0; i < buffers.size(); ++i) { const BufferInfo &info = buffers[i]; // skip undequeued buffers for meta data mode if (mStoreMetaDataInOutputBuffers && !mLegacyAdaptiveExperiment && info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGV("skipping buffer %p", info.mGraphicBuffer->getNativeBuffer()); continue; Loading @@ -632,7 +637,7 @@ status_t ACodec::handleSetSurface(const sp<Surface> &surface) { } // cancel undequeued buffers to new surface if (!mStoreMetaDataInOutputBuffers) { if (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment) { for (size_t i = 0; i < buffers.size(); ++i) { const BufferInfo &info = buffers[i]; if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) { Loading Loading @@ -967,6 +972,44 @@ status_t ACodec::allocateOutputMetaDataBuffers() { return err; mNumUndequeuedBuffers = minUndequeuedBuffers; if (mLegacyAdaptiveExperiment) { // preallocate buffers static_cast<Surface *>(mNativeWindow.get()) ->getIGraphicBufferProducer()->allowAllocation(true); ALOGV("[%s] Allocating %u buffers from a native window of size %u on " "output port", mComponentName.c_str(), bufferCount, bufferSize); // Dequeue buffers then cancel them all for (OMX_U32 i = 0; i < bufferCount; i++) { ANativeWindowBuffer *buf; err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf); if (err != 0) { ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err); break; } sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(buf, false)); BufferInfo info; info.mStatus = BufferInfo::OWNED_BY_US; info.mGraphicBuffer = graphicBuffer; mBuffers[kPortIndexOutput].push(info); } for (OMX_U32 i = 0; i < mBuffers[kPortIndexOutput].size(); i++) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); status_t error = cancelBufferToNativeWindow(info); if (err == OK) { err = error; } } mBuffers[kPortIndexOutput].clear(); static_cast<Surface*>(mNativeWindow.get()) ->getIGraphicBufferProducer()->allowAllocation(false); } ALOGV("[%s] Allocating %u meta buffers on output port", mComponentName.c_str(), bufferCount); Loading Loading @@ -1046,26 +1089,57 @@ ACodec::BufferInfo *ACodec::dequeueBufferFromNativeWindow() { return NULL; } do { if (native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &buf) != 0) { ALOGE("dequeueBuffer failed."); return NULL; } BufferInfo *oldest = NULL; bool stale = false; for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); if (info->mGraphicBuffer != NULL && info->mGraphicBuffer->handle == buf->handle) { CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_NATIVE_WINDOW); // Since consumers can attach buffers to BufferQueues, it is possible // that a known yet stale buffer can return from a surface that we // once used. We can simply ignore this as we have already dequeued // this buffer properly. NOTE: this does not eliminate all cases, // e.g. it is possible that we have queued the valid buffer to the // NW, and a stale copy of the same buffer gets dequeued - which will // be treated as the valid buffer by ACodec. if (info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) { ALOGI("dequeued stale buffer %p. discarding", buf); stale = true; break; } ALOGV("dequeued buffer %p", info->mGraphicBuffer->getNativeBuffer()); info->mStatus = BufferInfo::OWNED_BY_US; return info; } } // It is also possible to receive a previously unregistered buffer // in non-meta mode. These should be treated as stale buffers. The // same is possible in meta mode, in which case, it will be treated // as a normal buffer, which is not desirable. // TODO: fix this. if (!stale && (!mStoreMetaDataInOutputBuffers || mLegacyAdaptiveExperiment)) { ALOGI("dequeued unrecognized (stale) buffer %p. discarding", buf); stale = true; } if (stale) { // TODO: detach stale buffer, but there is no API yet to do it. buf = NULL; } } while (buf == NULL); // get oldest undequeued buffer BufferInfo *oldest = NULL; for (size_t i = mBuffers[kPortIndexOutput].size(); i-- > 0;) { BufferInfo *info = &mBuffers[kPortIndexOutput].editItemAt(i); if (info->mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW && (oldest == NULL || // avoid potential issues from counter rolling over Loading Loading @@ -1384,6 +1458,7 @@ status_t ACodec::configureCodec( bool haveNativeWindow = msg->findObject("native-window", &obj) && obj != NULL && video && !encoder; mStoreMetaDataInOutputBuffers = false; mLegacyAdaptiveExperiment = false; if (video && !encoder) { inputFormat->setInt32("adaptive-playback", false); Loading Loading @@ -1521,6 +1596,9 @@ status_t ACodec::configureCodec( ALOGV("[%s] storeMetaDataInBuffers succeeded", mComponentName.c_str()); mStoreMetaDataInOutputBuffers = true; mLegacyAdaptiveExperiment = ADebug::isExperimentEnabled( "legacy-adaptive", !msg->contains("no-experiments")); inputFormat->setInt32("adaptive-playback", true); } Loading Loading @@ -4135,7 +4213,9 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) { sp<RefBase> obj; CHECK(msg->findObject("surface", &obj)); status_t err = mCodec->handleSetSurface(static_cast<Surface *>(obj.get())); status_t err = ADebug::isExperimentEnabled("legacy-setsurface") ? BAD_VALUE : mCodec->handleSetSurface(static_cast<Surface *>(obj.get())); sp<AMessage> response = new AMessage; response->setInt32("err", err); Loading
media/libstagefright/Android.mk +1 −1 Original line number Diff line number Diff line Loading @@ -124,7 +124,7 @@ LOCAL_SHARED_LIBRARIES += \ libdl \ libRScpp \ LOCAL_CFLAGS += -Wno-multichar -Werror -Wall LOCAL_CFLAGS += -Wno-multichar -Werror -Wall -DENABLE_STAGEFRIGHT_EXPERIMENTS LOCAL_CLANG := true LOCAL_MODULE:= libstagefright Loading
media/libstagefright/foundation/ADebug.cpp +39 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <ctype.h> #define LOG_TAG "ADebug" #include <cutils/atomic.h> #include <utils/Log.h> #include <utils/misc.h> Loading Loading @@ -113,5 +114,43 @@ char *ADebug::GetDebugName(const char *name) { return debugName; } //static bool ADebug::getExperimentFlag( bool allow, const char *name, uint64_t modulo, uint64_t limit, uint64_t plus, uint64_t timeDivisor) { static volatile int32_t haveSerial = 0; static uint64_t serialNum; if (!android_atomic_acquire_load(&haveSerial)) { // calculate initial counter value based on serial number static char serial[PROPERTY_VALUE_MAX]; property_get("ro.serialno", serial, "0"); uint64_t num = 0; // it is okay for this number to overflow for (size_t i = 0; i < NELEM(serial) && serial[i] != '\0'; ++i) { const char &c = serial[i]; // try to use most letters of serialno if (isdigit(c)) { num = num * 10 + (c - '0'); } else if (islower(c)) { num = num * 26 + (c - 'a'); } else if (isupper(c)) { num = num * 26 + (c - 'A'); } else { num = num * 256 + c; } } ALOGI("got serial"); serialNum = num; android_atomic_release_store(1, &haveSerial); } ALOGI("serial: %llu, time: %llu", (long long)serialNum, (long long)time(NULL)); // MINOR: use modulo for counter and time, so that their sum does not // roll over, and mess up the correlation between related experiments. // e.g. keep (a mod 2N) = 0 impl (a mod N) = 0 time_t counter = (time(NULL) / timeDivisor) % modulo + plus + serialNum % modulo; bool enable = allow && (counter % modulo < limit); ALOGI("experiment '%s': %s", name, enable ? "ENABLED" : "disabled"); return enable; } } // namespace android