Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cbe81585 authored by Manikanta Kanamarlapudi's avatar Manikanta Kanamarlapudi Committed by Steve Kondik
Browse files

libstagefright: Fix to free buffers in error scenarios

- MediaBuffer objects are not freed if component transitions from
  IDLE->EXECUTING or EXECUTING->LOADED fails (or if the
  OMX_CmdComplete messages are dropped due to an earlier error)
  Additionally with native buffers, the corresponding
  ANativeWindowBuffers are not cancelled back in the above scenarios.

CRs-Fixed: 640236

Change-Id: I62bdcde5d52ee65cba457a9b5e938e0390d8d9b4
parent 68bbefd3
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 * Not a Contribution.
 *
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
@@ -391,6 +394,8 @@ private:
            unsigned *profile, unsigned *level);

    status_t stopOmxComponent_l();
    status_t flushBuffersOnError();
    status_t releaseMediaBuffersOn(OMX_U32 portIndex);

    OMXCodec(const OMXCodec &);
    OMXCodec &operator=(const OMXCodec &);
+118 −0
Original line number Diff line number Diff line
@@ -1949,6 +1949,10 @@ OMXCodec::~OMXCodec() {
    CHECK_EQ(err, (status_t)OK);

    mNode = NULL;

    releaseMediaBuffersOn(kPortIndexOutput);
    releaseMediaBuffersOn(kPortIndexInput);

    setState(DEAD);

    clearCodecSpecificData();
@@ -1990,6 +1994,12 @@ status_t OMXCodec::init() {
        mAsyncCompletion.wait(mLock);
    }

    // If the native window is valid, we need to do the extra work of
    // cancelling buffers back.
    if (mState == ERROR) {
        flushBuffersOnError();
    }

    return mState == ERROR ? UNKNOWN_ERROR : OK;
}

@@ -4911,6 +4921,7 @@ status_t OMXCodec::stopOmxComponent_l() {
    }

    bool isError = false;
    bool forceFlush = false;
    switch (mState) {
        case LOADED:
            break;
@@ -4939,6 +4950,7 @@ status_t OMXCodec::stopOmxComponent_l() {
                CHECK_EQ(err, (status_t)OK);

                if (state != OMX_StateExecuting) {
                    forceFlush = true;
                    break;
                }
                // else fall through to the idling code
@@ -4991,6 +5003,10 @@ status_t OMXCodec::stopOmxComponent_l() {
                mAsyncCompletion.wait(mLock);
            }

            if (mState == ERROR) {
                forceFlush = true;
            }

            if (isError) {
                // We were in the ERROR state coming in, so restore that now
                // that we've idled the OMX component.
@@ -5007,6 +5023,10 @@ status_t OMXCodec::stopOmxComponent_l() {
        }
    }

    if (forceFlush) {
        flushBuffersOnError();
    }

    if (mLeftOverBuffer) {
        mLeftOverBuffer->release();
        mLeftOverBuffer = NULL;
@@ -6157,4 +6177,102 @@ bool OMXCodec::hasDisabledPorts() {
    }
    return true;
}
status_t OMXCodec::releaseMediaBuffersOn(OMX_U32 portIndex) {
    if (mPortBuffers[portIndex].size() == 0) {
        return OK;
    }

    if (mState != ERROR) {
        CODEC_LOGE("assertion failure, needs to be investigated why %s "
              " buffers are still pending",
              portIndex == kPortIndexOutput ? "output" : "input");
    }

    Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];

    for (size_t i = buffers->size(); i-- > 0;) {
        BufferInfo *info = &buffers->editItemAt(i);
        if (info->mMediaBuffer) {
            if (portIndex != (OMX_U32)kPortIndexOutput) {
                return UNKNOWN_ERROR;
            }
            info->mMediaBuffer->setObserver(NULL);

            // Make sure nobody but us owns this buffer at this point.
            if (info->mMediaBuffer->refcount() != 0) {
                return UNKNOWN_ERROR;
            }

            info->mMediaBuffer->release();
            info->mMediaBuffer = NULL;
        }
        buffers->removeAt(i);
    }
    return OK;
}

// Last resort to flush buffers and additionally cancel all native window buffers.
//lock _must_ be acquired in caller
status_t OMXCodec::flushBuffersOnError() {
    if (mState != ERROR) {
        return INVALID_OPERATION;
    }

    OMX_STATETYPE state = OMX_StateInvalid;
    status_t err = mOMX->getState(mNode, &state);
    if (err != OK) { //component is alive
        return err;
    }

    mPortStatus[kPortIndexOutput] = ENABLED;
    mPortStatus[kPortIndexInput] = ENABLED;

    setState(EXECUTING_TO_IDLE);

    flushPortAsync(kPortIndexOutput);
    flushPortAsync(kPortIndexInput);

    size_t kRetries = 15;

    bool outputBuffersPending =
        countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) !=
        mPortBuffers[kPortIndexOutput].size();

    bool inputBuffersPending =
        countBuffersWeOwn(mPortBuffers[kPortIndexInput]) !=
        mPortBuffers[kPortIndexInput].size();

    setState(ERROR); //drop all except EBD/FBD
    while ((outputBuffersPending || inputBuffersPending) && --kRetries) {
        mLock.unlock();
        usleep(10000);
        mLock.lock();

        outputBuffersPending =
            countBuffersWeOwn(mPortBuffers[kPortIndexOutput]) !=
            mPortBuffers[kPortIndexOutput].size();

        inputBuffersPending =
            countBuffersWeOwn(mPortBuffers[kPortIndexInput]) !=
            mPortBuffers[kPortIndexInput].size();
    }

    if (inputBuffersPending || outputBuffersPending) {
        ALOGE("Timed out waiting for all input/output buffers to be returned, "
              "there might be a leak");
    }

    //additional work for native buffers
    if (mNativeWindow != NULL) {
        Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
        for (size_t i = 0; i < buffers->size(); ++i) {
            BufferInfo *info = &buffers->editItemAt(i);
            if (info->mStatus == OWNED_BY_US) {
                cancelBufferToNativeWindow(info);
            }
        }
    }

    return OK;
}
}  // namespace android