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

Commit 8651c6f5 authored by Lajos Molnar's avatar Lajos Molnar Committed by Android (Google) Code Review
Browse files

Merge "stagefright: add support for dynamically setting MediaCodec output surface" into mnc-dev

parents e2a2dfcb 1dcdfead
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ struct ACodec : public AHierarchicalStateMachine, public CodecBase {
    virtual void initiateStart();
    virtual void initiateShutdown(bool keepComponentAllocated = false);

    virtual status_t setSurface(const sp<Surface> &surface);

    virtual void signalFlush();
    virtual void signalResume();

@@ -115,6 +117,7 @@ private:
        kWhatDrainDeferredMessages   = 'drai',
        kWhatAllocateComponent       = 'allo',
        kWhatConfigureComponent      = 'conf',
        kWhatSetSurface              = 'setS',
        kWhatCreateInputSurface      = 'cisf',
        kWhatUsePersistentInputSurface = 'pisf',
        kWhatSignalEndOfInputStream  = 'eois',
@@ -232,6 +235,12 @@ private:
    status_t freeBuffersOnPort(OMX_U32 portIndex);
    status_t freeBuffer(OMX_U32 portIndex, size_t i);

    status_t handleSetSurface(const sp<Surface> &surface);
    status_t setNativeWindowSizeFormatAndUsage(
            ANativeWindow *nativeWindow /* nonnull */,
            int width, int height, int format, int rotation, int usage);
    status_t setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */);

    status_t configureOutputBuffersFromNativeWindow(
            OMX_U32 *nBufferCount, OMX_U32 *nBufferSize,
            OMX_U32 *nMinUndequeuedBuffers);
+2 −0
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ struct CodecBase : public AHandler {
    // require an explicit message handler
    virtual void onMessageReceived(const sp<AMessage> &msg) = 0;

    virtual status_t setSurface(const sp<Surface> &surface) { return INVALID_OPERATION; }

    virtual void signalFlush() = 0;
    virtual void signalResume() = 0;

+6 −2
Original line number Diff line number Diff line
@@ -146,6 +146,8 @@ struct MediaCodec : public AHandler {
    status_t getOutputFormat(size_t index, sp<AMessage> *format);
    status_t getInputBuffer(size_t index, sp<ABuffer> *buffer);

    status_t setSurface(const sp<Surface> &nativeWindow);

    status_t requestIDRFrame();

    // Notification will be posted once there "is something to do", i.e.
@@ -184,6 +186,7 @@ private:
    enum {
        kWhatInit                           = 'init',
        kWhatConfigure                      = 'conf',
        kWhatSetSurface                     = 'sSur',
        kWhatCreateInputSurface             = 'cisf',
        kWhatUsePersistentInputSurface      = 'pisf',
        kWhatStart                          = 'strt',
@@ -340,8 +343,9 @@ private:
    void extractCSD(const sp<AMessage> &format);
    status_t queueCSDInputBuffer(size_t bufferIndex);

    status_t handleSetSurface(
            const sp<Surface> &surface);
    status_t handleSetSurface(const sp<Surface> &surface);
    status_t connectToSurface(const sp<Surface> &surface);
    status_t disconnectFromSurface();

    void postActivityNotificationIfPossible();

+228 −98
Original line number Diff line number Diff line
@@ -479,6 +479,19 @@ void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {
    msg->post();
}

status_t ACodec::setSurface(const sp<Surface> &surface) {
    sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
    msg->setObject("surface", surface);

    sp<AMessage> response;
    status_t err = msg->postAndAwaitResponse(&response);

    if (err == OK) {
        (void)response->findInt32("err", &err);
    }
    return err;
}

void ACodec::initiateCreateInputSurface() {
    (new AMessage(kWhatCreateInputSurface, this))->post();
}
@@ -533,6 +546,114 @@ void ACodec::signalSubmitOutputMetaDataBufferIfEOS_workaround() {
    }
}

status_t ACodec::handleSetSurface(const sp<Surface> &surface) {
    // allow keeping unset surface
    if (surface == NULL) {
        if (mNativeWindow != NULL) {
            ALOGW("cannot unset a surface");
            return INVALID_OPERATION;
        }
        return OK;
    }

    // allow keeping unset surface
    if (mNativeWindow == NULL) {
        ALOGW("component was not configured with a surface");
        return INVALID_OPERATION;
    }

    ANativeWindow *nativeWindow = surface.get();
    // if we have not yet started the codec, we can simply set the native window
    if (mBuffers[kPortIndexInput].size() == 0) {
        mNativeWindow = surface;
        return OK;
    }

    // we do not support changing a tunneled surface after start
    if (mTunneled) {
        ALOGW("cannot change tunneled surface");
        return INVALID_OPERATION;
    }

    status_t err = setupNativeWindowSizeFormatAndUsage(nativeWindow);
    if (err != OK) {
        return err;
    }

    // get min undequeued count. We cannot switch to a surface that has a higher
    // undequeued count than we allocated.
    int minUndequeuedBuffers = 0;
    err = nativeWindow->query(
            nativeWindow, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
            &minUndequeuedBuffers);
    if (err != 0) {
        ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
                strerror(-err), -err);
        return err;
    }
    if (minUndequeuedBuffers > (int)mNumUndequeuedBuffers) {
        ALOGE("new surface holds onto more buffers (%d) than planned for (%zu)",
                minUndequeuedBuffers, mNumUndequeuedBuffers);
        return BAD_VALUE;
    }

    // we cannot change the number of output buffers while OMX is running
    // set up surface to the same count
    Vector<BufferInfo> &buffers = mBuffers[kPortIndexOutput];
    ALOGV("setting up surface for %zu buffers", buffers.size());

    err = native_window_set_buffer_count(nativeWindow, buffers.size());
    if (err != 0) {
        ALOGE("native_window_set_buffer_count failed: %s (%d)", strerror(-err),
                -err);
        return err;
    }

    // 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
                && info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
            ALOGV("skipping buffer %p", info.mGraphicBuffer->getNativeBuffer());
            continue;
        }
        ALOGV("attaching buffer %p", info.mGraphicBuffer->getNativeBuffer());

        err = surface->attachBuffer(info.mGraphicBuffer->getNativeBuffer());
        if (err != OK) {
            ALOGE("failed to attach buffer %p to the new surface: %s (%d)",
                    info.mGraphicBuffer->getNativeBuffer(),
                    strerror(-err), -err);
            return err;
        }
    }

    // cancel undequeued buffers to new surface
    if (!mStoreMetaDataInOutputBuffers) {
        for (size_t i = 0; i < buffers.size(); ++i) {
            const BufferInfo &info = buffers[i];
            if (info.mStatus == BufferInfo::OWNED_BY_NATIVE_WINDOW) {
                ALOGV("canceling buffer %p", info.mGraphicBuffer->getNativeBuffer());
                err = nativeWindow->cancelBuffer(
                        nativeWindow, info.mGraphicBuffer->getNativeBuffer(), -1);
                if (err != OK) {
                    ALOGE("failed to cancel buffer %p to the new surface: %s (%d)",
                            info.mGraphicBuffer->getNativeBuffer(),
                            strerror(-err), -err);
                    return err;
                }
            }
        }
        // disallow further allocation
        (void)surface->getIGraphicBufferProducer()->allowAllocation(false);
    }

    mNativeWindow = nativeWindow;
    return OK;
}

status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);

@@ -627,74 +748,35 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
    return OK;
}

status_t ACodec::configureOutputBuffersFromNativeWindow(
        OMX_U32 *bufferCount, OMX_U32 *bufferSize,
        OMX_U32 *minUndequeuedBuffers) {
    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = kPortIndexOutput;

    status_t err = mOMX->getParameter(
            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));

    if (err != OK) {
        return err;
    }

    err = native_window_set_buffers_dimensions(
            mNativeWindow.get(),
            def.format.video.nFrameWidth,
            def.format.video.nFrameHeight);

status_t ACodec::setNativeWindowSizeFormatAndUsage(
        ANativeWindow *nativeWindow /* nonnull */,
        int width, int height, int format, int rotation, int usage) {
    status_t err = native_window_set_buffers_dimensions(nativeWindow, width, height);
    if (err != 0) {
        ALOGE("native_window_set_buffers_dimensions failed: %s (%d)",
                strerror(-err), -err);
        ALOGE("native_window_set_buffers_dimensions failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    err = native_window_set_buffers_format(
            mNativeWindow.get(),
            def.format.video.eColorFormat);

    err = native_window_set_buffers_format(nativeWindow, format);
    if (err != 0) {
        ALOGE("native_window_set_buffers_format failed: %s (%d)",
                strerror(-err), -err);
        ALOGE("native_window_set_buffers_format failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    if (mRotationDegrees != 0) {
        uint32_t transform = 0;
        switch (mRotationDegrees) {
            case 0: transform = 0; break;
            case 90: transform = HAL_TRANSFORM_ROT_90; break;
            case 180: transform = HAL_TRANSFORM_ROT_180; break;
            case 270: transform = HAL_TRANSFORM_ROT_270; break;
    int transform = 0;
    if ((rotation % 90) == 0) {
        switch ((rotation / 90) & 3) {
            case 1:  transform = HAL_TRANSFORM_ROT_90;  break;
            case 2:  transform = HAL_TRANSFORM_ROT_180; break;
            case 3:  transform = HAL_TRANSFORM_ROT_270; break;
            default: transform = 0;                     break;
        }

        if (transform > 0) {
            err = native_window_set_buffers_transform(
                    mNativeWindow.get(), transform);
            if (err != 0) {
                ALOGE("native_window_set_buffers_transform failed: %s (%d)",
                        strerror(-err), -err);
                return err;
            }
        }
    }

    // Set up the native window.
    OMX_U32 usage = 0;
    err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
    err = native_window_set_buffers_transform(nativeWindow, transform);
    if (err != 0) {
        ALOGW("querying usage flags from OMX IL component failed: %d", err);
        // XXX: Currently this error is logged, but not fatal.
        usage = 0;
    }
    int omxUsage = usage;

    if (mFlags & kFlagIsGrallocUsageProtected) {
        usage |= GRALLOC_USAGE_PROTECTED;
        ALOGE("native_window_set_buffers_transform failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    // Make sure to check whether either Stagefright or the video decoder
@@ -703,11 +785,10 @@ status_t ACodec::configureOutputBuffersFromNativeWindow(
        // Verify that the ANativeWindow sends images directly to
        // SurfaceFlinger.
        int queuesToNativeWindow = 0;
        err = mNativeWindow->query(
                mNativeWindow.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER,
                &queuesToNativeWindow);
        err = nativeWindow->query(
                nativeWindow, NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &queuesToNativeWindow);
        if (err != 0) {
            ALOGE("error authenticating native window: %d", err);
            ALOGE("error authenticating native window: %s (%d)", strerror(-err), -err);
            return err;
        }
        if (queuesToNativeWindow != 1) {
@@ -717,26 +798,84 @@ status_t ACodec::configureOutputBuffersFromNativeWindow(
    }

    int consumerUsage = 0;
    err = mNativeWindow->query(
            mNativeWindow.get(), NATIVE_WINDOW_CONSUMER_USAGE_BITS,
            &consumerUsage);
    err = nativeWindow->query(nativeWindow, NATIVE_WINDOW_CONSUMER_USAGE_BITS, &consumerUsage);
    if (err != 0) {
        ALOGW("failed to get consumer usage bits. ignoring");
        err = 0;
    }

    ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec) + %#x(Consumer) = %#x",
            omxUsage, usage, consumerUsage, usage | consumerUsage);
    usage |= consumerUsage;
    err = native_window_set_usage(
            mNativeWindow.get(),
            usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);

    int finalUsage = usage | consumerUsage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP;
    ALOGV("gralloc usage: %#x(ACodec) + %#x(Consumer) = %#x", usage, consumerUsage, finalUsage);
    err = native_window_set_usage(nativeWindow, finalUsage);
    if (err != 0) {
        ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    err = native_window_set_scaling_mode(
            nativeWindow, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (err != 0) {
        ALOGE("native_window_set_scaling_mode failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    ALOGD("set up nativeWindow %p for %dx%d, color %#x, rotation %d, usage %#x",
            nativeWindow, width, height, format, rotation, finalUsage);
    return OK;
}

status_t ACodec::setupNativeWindowSizeFormatAndUsage(ANativeWindow *nativeWindow /* nonnull */) {
    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = kPortIndexOutput;

    status_t err = mOMX->getParameter(
            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));

    if (err != OK) {
        return err;
    }

    OMX_U32 usage = 0;
    err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
    if (err != 0) {
        ALOGW("querying usage flags from OMX IL component failed: %d", err);
        // XXX: Currently this error is logged, but not fatal.
        usage = 0;
    }
    int omxUsage = usage;

    if (mFlags & kFlagIsGrallocUsageProtected) {
        usage |= GRALLOC_USAGE_PROTECTED;
    }

    ALOGV("gralloc usage: %#x(OMX) => %#x(ACodec)", omxUsage, usage);
    return setNativeWindowSizeFormatAndUsage(
            nativeWindow,
            def.format.video.nFrameWidth,
            def.format.video.nFrameHeight,
            def.format.video.eColorFormat,
            mRotationDegrees,
            usage);
}

status_t ACodec::configureOutputBuffersFromNativeWindow(
        OMX_U32 *bufferCount, OMX_U32 *bufferSize,
        OMX_U32 *minUndequeuedBuffers) {
    OMX_PARAM_PORTDEFINITIONTYPE def;
    InitOMXParams(&def);
    def.nPortIndex = kPortIndexOutput;

    status_t err = mOMX->getParameter(
            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));

    if (err == OK) {
        err = setupNativeWindowSizeFormatAndUsage(mNativeWindow.get());
    }
    if (err != OK) {
        return err;
    }

    // Exits here for tunneled video playback codecs -- i.e. skips native window
    // buffer allocation step as this is managed by the tunneled OMX omponent
    // itself and explicitly sets def.nBufferCountActual to 0.
@@ -1489,9 +1628,6 @@ status_t ACodec::configureCodec(
        if (haveNativeWindow) {
            mNativeWindow = static_cast<Surface *>(obj.get());
            CHECK(mNativeWindow != NULL);

            native_window_set_scaling_mode(
                    mNativeWindow.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
        }

        // initialize native window now to get actual output format
@@ -4012,32 +4148,10 @@ status_t ACodec::pushBlankBuffersToNativeWindow() {
        return err;
    }

    err = native_window_set_buffers_dimensions(mNativeWindow.get(), 1, 1);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: set_buffers_dimensions failed: %s (%d)",
                strerror(-err), -err);
        goto error;
    }

    err = native_window_set_buffers_format(mNativeWindow.get(), HAL_PIXEL_FORMAT_RGBX_8888);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: set_buffers_format failed: %s (%d)",
                strerror(-err), -err);
        goto error;
    }

    err = native_window_set_scaling_mode(mNativeWindow.get(),
                NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank_frames: set_scaling_mode failed: %s (%d)",
              strerror(-err), -err);
        goto error;
    }

    err = native_window_set_usage(mNativeWindow.get(),
            GRALLOC_USAGE_SW_WRITE_OFTEN);
    err = setNativeWindowSizeFormatAndUsage(
            mNativeWindow.get(), 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: set_usage failed: %s (%d)",
        ALOGE("error pushing blank frames: set format failed: %s (%d)",
                strerror(-err), -err);
        goto error;
    }
@@ -4209,6 +4323,22 @@ bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
            return onOMXMessage(msg);
        }

        case ACodec::kWhatSetSurface:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            sp<RefBase> obj;
            CHECK(msg->findObject("surface", &obj));

            status_t err = mCodec->handleSetSurface(static_cast<Surface *>(obj.get()));

            sp<AMessage> response = new AMessage;
            response->setInt32("err", err);
            response->postReply(replyID);
            break;
        }

        case ACodec::kWhatCreateInputSurface:
        case ACodec::kWhatUsePersistentInputSurface:
        case ACodec::kWhatSignalEndOfInputStream:
+93 −22
Original line number Diff line number Diff line
@@ -556,6 +556,14 @@ status_t MediaCodec::usePersistentInputSurface(
    return PostAndAwaitResponse(msg, &response);
}

status_t MediaCodec::setSurface(const sp<Surface> &surface) {
    sp<AMessage> msg = new AMessage(kWhatSetSurface, this);
    msg->setObject("surface", surface);

    sp<AMessage> response;
    return PostAndAwaitResponse(msg, &response);
}

status_t MediaCodec::createInputSurface(
        sp<IGraphicBufferProducer>* bufferProducer) {
    sp<AMessage> msg = new AMessage(kWhatCreateInputSurface, this);
@@ -1249,7 +1257,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                {
                    // response to initiateCreateInputSurface()
                    status_t err = NO_ERROR;
                    sp<AMessage> response = new AMessage();
                    sp<AMessage> response = new AMessage;
                    if (!msg->findInt32("err", &err)) {
                        sp<RefBase> obj;
                        msg->findObject("input-surface", &obj);
@@ -1280,7 +1288,7 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
                case CodecBase::kWhatSignaledInputEOS:
                {
                    // response to signalEndOfInputStream()
                    sp<AMessage> response = new AMessage();
                    sp<AMessage> response = new AMessage;
                    status_t err;
                    if (msg->findInt32("err", &err)) {
                        response->setInt32("err", err);
@@ -1686,6 +1694,61 @@ void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
            break;
        }

        case kWhatSetSurface:
        {
            sp<AReplyToken> replyID;
            CHECK(msg->senderAwaitsResponse(&replyID));

            status_t err = OK;
            sp<Surface> surface;

            switch (mState) {
                case CONFIGURED:
                case STARTED:
                case FLUSHED:
                {
                    sp<RefBase> obj;
                    (void)msg->findObject("surface", &obj);
                    sp<Surface> surface = static_cast<Surface *>(obj.get());
                    if (mSurface == NULL) {
                        // do not support setting surface if it was not set
                        err = INVALID_OPERATION;
                    } else if (obj == NULL) {
                        // do not support unsetting surface
                        err = BAD_VALUE;
                    } else {
                        err = connectToSurface(surface);
                        if (err == BAD_VALUE) {
                            // assuming reconnecting to same surface
                            // TODO: check if it is the same surface
                            err = OK;
                        } else {
                            if (err == OK) {
                                if (mFlags & kFlagUsesSoftwareRenderer) {
                                    mSoftRenderer = new SoftwareRenderer(surface);
                                    // TODO: check if this was successful
                                } else {
                                    err = mCodec->setSurface(surface);
                                }
                            }
                            if (err == OK) {
                                (void)disconnectFromSurface();
                                mSurface = surface;
                            }
                        }
                    }
                    break;
                }

                default:
                    err = INVALID_OPERATION;
                    break;
            }

            PostReplyWithError(replyID, err);
            break;
        }

        case kWhatCreateInputSurface:
        case kWhatUsePersistentInputSurface:
        {
@@ -2456,36 +2519,44 @@ ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
    return index;
}

status_t MediaCodec::handleSetSurface(const sp<Surface> &surface) {
    status_t err;
status_t MediaCodec::connectToSurface(const sp<Surface> &surface) {
    status_t err = OK;
    if (surface != NULL) {
        err = native_window_api_connect(surface.get(), NATIVE_WINDOW_API_MEDIA);
        if (err == BAD_VALUE) {
            ALOGI("native window already connected. Assuming no change of surface");
        } else if (err != OK) {
            ALOGE("native_window_api_connect returned an error: %s (%d)", strerror(-err), err);
        }
    }
    return err;
}

status_t MediaCodec::disconnectFromSurface() {
    status_t err = OK;
    if (mSurface != NULL) {
        err = native_window_api_disconnect(
                mSurface.get(), NATIVE_WINDOW_API_MEDIA);

        err = native_window_api_disconnect(mSurface.get(), NATIVE_WINDOW_API_MEDIA);
        if (err != OK) {
            ALOGW("native_window_api_disconnect returned an error: %s (%d)",
                    strerror(-err), err);
            ALOGW("native_window_api_disconnect returned an error: %s (%d)", strerror(-err), err);
        }

        // assume disconnected even on error
        mSurface.clear();
    }

    if (surface != NULL) {
        err = native_window_api_connect(
                surface.get(), NATIVE_WINDOW_API_MEDIA);

        if (err != OK) {
            ALOGE("native_window_api_connect returned an error: %s (%d)",
                    strerror(-err), err);

    return err;
}

status_t MediaCodec::handleSetSurface(const sp<Surface> &surface) {
    status_t err = OK;
    if (mSurface != NULL) {
        (void)disconnectFromSurface();
    }
    if (surface != NULL) {
        err = connectToSurface(surface);
        if (err == OK) {
            mSurface = surface;
        }

    return OK;
    }
    return err;
}

void MediaCodec::onInputBufferAvailable() {