C++程序  |  313行  |  11.16 KB

/*
 * Copyright 2018, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "C2VdaBqBlockPool"

#include <errno.h>

#include <mutex>

#include <gui/BufferQueueDefs.h>
#include <utils/Log.h>

#include <C2AllocatorGralloc.h>
#include <C2BlockInternal.h>
#include <v4l2/C2VdaBqBlockPool.h>

using ::android::AnwBuffer;
using ::android::C2AndroidMemoryUsage;
using ::android::Fence;
using ::android::GraphicBuffer;
using ::android::HGraphicBufferProducer;
using ::android::hidl_handle;
using ::android::IGraphicBufferProducer;
using ::android::sp;
using ::android::status_t;
using ::android::hardware::graphics::common::V1_0::PixelFormat;

namespace {

// The wait time for acquire fence in milliseconds.
const int kFenceWaitTimeMs = 10;
// The timeout delay for dequeuing buffer from producer in nanoseconds.
const int64_t kDequeueTimeoutNs = 10 * 1000 * 1000;

}  // namespace

static c2_status_t asC2Error(int32_t err) {
    switch (err) {
    case android::NO_ERROR:
        return C2_OK;
    case android::NO_INIT:
        return C2_NO_INIT;
    case android::BAD_VALUE:
        return C2_BAD_VALUE;
    case android::TIMED_OUT:
        return C2_TIMED_OUT;
    case android::WOULD_BLOCK:
        return C2_BLOCKING;
    case android::NO_MEMORY:
        return C2_NO_MEMORY;
    case -ETIME:
        return C2_TIMED_OUT;  // for fence wait
    }
    return C2_CORRUPTED;
}

C2VdaBqBlockPool::C2VdaBqBlockPool(const std::shared_ptr<C2Allocator>& allocator,
                                   const local_id_t localId)
      : C2BufferQueueBlockPool(allocator, localId),
        mAllocator(allocator),
        mLocalId(localId),
        mMaxDequeuedBuffers(0u) {}

C2VdaBqBlockPool::~C2VdaBqBlockPool() {
    std::lock_guard<std::mutex> lock(mMutex);
    cancelAllBuffers();
}

c2_status_t C2VdaBqBlockPool::fetchGraphicBlock(
        uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
        std::shared_ptr<C2GraphicBlock>* block /* nonnull */) {
    std::lock_guard<std::mutex> lock(mMutex);

    if (!mProducer) {
        // Producer will not be configured in byte-buffer mode. Allocate buffers from allocator
        // directly as a basic graphic block pool.
        std::shared_ptr<C2GraphicAllocation> alloc;
        c2_status_t err = mAllocator->newGraphicAllocation(width, height, format, usage, &alloc);
        if (err != C2_OK) {
            return err;
        }
        *block = _C2BlockFactory::CreateGraphicBlock(alloc);
        return C2_OK;
    }

    sp<Fence> fence = new Fence();
    C2AndroidMemoryUsage androidUsage = usage;
    int32_t status;
    PixelFormat pixelFormat = static_cast<PixelFormat>(format);
    int32_t slot;

    mProducer->dequeueBuffer(
            width, height, pixelFormat, androidUsage.asGrallocUsage(), true,
            [&status, &slot, &fence](int32_t tStatus, int32_t tSlot, hidl_handle const& tFence,
                                     HGraphicBufferProducer::FrameEventHistoryDelta const& tTs) {
                status = tStatus;
                slot = tSlot;
                if (!android::conversion::convertTo(fence.get(), tFence) &&
                    status == android::NO_ERROR) {
                    status = android::BAD_VALUE;
                }
                (void)tTs;
            });

    // check dequeueBuffer return flag
    if (status != android::NO_ERROR &&
        status != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
        if (status == android::TIMED_OUT) {
            // no buffer is available now, wait for another retry.
            ALOGV("dequeueBuffer timed out, wait for retry...");
            return C2_TIMED_OUT;
        }
        ALOGE("dequeueBuffer failed: %d", status);
        return asC2Error(status);
    }

    // wait for acquire fence if we get one.
    native_handle_t* nh = nullptr;
    hidl_handle fenceHandle;
    if (fence) {
        android::conversion::wrapAs(&fenceHandle, &nh, *fence);
        status_t fenceStatus = fence->wait(kFenceWaitTimeMs);
        if (fenceStatus != android::NO_ERROR) {
            ALOGE("buffer fence wait error: %d", fenceStatus);
            mProducer->cancelBuffer(slot, fenceHandle);
            native_handle_delete(nh);
            return asC2Error(fenceStatus);
        }
    }

    auto iter = mSlotAllocations.find(slot);
    if (iter == mSlotAllocations.end()) {
        // it's a new slot index, request for a new buffer.
        if (mSlotAllocations.size() >= mMaxDequeuedBuffers) {
            ALOGE("still get a new slot index but already allocated enough buffers.");
            mProducer->cancelBuffer(slot, fenceHandle);
            native_handle_delete(nh);
            return C2_CORRUPTED;
        }
        if (status != IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) {
            ALOGE("expect BUFFER_NEEDS_REALLOCATION flag but didn't get one.");
            mProducer->cancelBuffer(slot, fenceHandle);
            native_handle_delete(nh);
            return C2_CORRUPTED;
        }
        sp<GraphicBuffer> slotBuffer = new GraphicBuffer();
        mProducer->requestBuffer(
                slot, [&status, &slotBuffer](int32_t tStatus, AnwBuffer const& tBuffer) {
                    status = tStatus;
                    if (!android::conversion::convertTo(slotBuffer.get(), tBuffer) &&
                        status == android::NO_ERROR) {
                        status = android::BAD_VALUE;
                    }
                });

        // check requestBuffer return flag
        if (status != android::NO_ERROR) {
            ALOGE("requestBuffer failed: %d", status);
            mProducer->cancelBuffer(slot, fenceHandle);
            native_handle_delete(nh);
            return asC2Error(status);
        }
        native_handle_delete(nh);

        // convert GraphicBuffer to C2GraphicAllocation and wrap producer id and slot index
        native_handle_t* grallocHandle = native_handle_clone(slotBuffer->handle);
        ALOGV("buffer wraps { producer id: %" PRIu64 ", slot: %d }", mProducerId, slot);
        C2Handle* c2Handle = android::WrapNativeCodec2GrallocHandle(
                grallocHandle, slotBuffer->width, slotBuffer->height, slotBuffer->format,
                slotBuffer->usage, slotBuffer->stride, mProducerId, slot);
        native_handle_delete(grallocHandle);
        if (!c2Handle) {
            ALOGE("WrapNativeCodec2GrallocHandle failed");
            return C2_NO_MEMORY;
        }

        std::shared_ptr<C2GraphicAllocation> alloc;
        c2_status_t err = mAllocator->priorGraphicAllocation(c2Handle, &alloc);
        if (err != C2_OK) {
            ALOGE("priorGraphicAllocation failed: %d", err);
            return err;
        }

        mSlotAllocations[slot] = std::move(alloc);
        if (mSlotAllocations.size() == mMaxDequeuedBuffers) {
            // already allocated enough buffers, set allowAllocation to false to restrict the
            // eligible slots to allocated ones for future dequeue.
            status = mProducer->allowAllocation(false);
            if (status != android::NO_ERROR) {
                ALOGE("allowAllocation(false) failed");
                return asC2Error(status);
            }
        }
    } else if (mSlotAllocations.size() < mMaxDequeuedBuffers) {
        ALOGE("failed to allocate enough buffers");
        return C2_BAD_STATE;
    }

    *block = _C2BlockFactory::CreateGraphicBlock(mSlotAllocations[slot]);
    return C2_OK;
}

c2_status_t C2VdaBqBlockPool::requestNewBufferSet(int32_t bufferCount) {
    if (bufferCount <= 0) {
        ALOGE("Invalid requested buffer count = %d", bufferCount);
        return C2_BAD_VALUE;
    }

    std::lock_guard<std::mutex> lock(mMutex);
    if (!mProducer) {
        ALOGD("No HGraphicBufferProducer is configured...");
        return C2_NO_INIT;
    }

    // For dynamic resolution change, cancel all mapping buffers and discard references.
    // Client needs to make sure all buffers are dequeued and owned by client before calling.
    auto cancelStatus = cancelAllBuffers();
    if (cancelStatus != C2_OK) {
        ALOGE("cancelBuffer failed while requesting new buffer set...");
        return C2_CORRUPTED;
    }

    // TODO: should we query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS) and add it on?
    int32_t status = mProducer->setMaxDequeuedBufferCount(bufferCount);
    if (status != android::NO_ERROR) {
        ALOGE("setMaxDequeuedBufferCount failed");
        return asC2Error(status);
    }
    mMaxDequeuedBuffers = static_cast<size_t>(bufferCount);

    status = mProducer->allowAllocation(true);
    if (status != android::NO_ERROR) {
        ALOGE("allowAllocation(true) failed");
        return asC2Error(status);
    }
    return C2_OK;
}

void C2VdaBqBlockPool::configureProducer(const sp<HGraphicBufferProducer>& producer) {
    ALOGV("configureProducer");
    std::lock_guard<std::mutex> lock(mMutex);
    // TODO: handle producer change request (client changes surface) while codec is running.
    if (mProducer) {
        ALOGE("Not allowed to reset HGraphicBufferProducer");
        return;
    }

    mProducer = producer;
    if (producer) {
        int32_t status;
        producer->getUniqueId(
                [&status, &producerId = mProducerId](int32_t tStatus, int64_t tProducerId) {
                    status = tStatus;
                    producerId = tProducerId;
                });
        if (status != android::NO_ERROR) {
            ALOGE("getUniqueId failed");
            mProducer = nullptr;
            mProducerId = 0;
            return;
        }
        status = producer->setDequeueTimeout(kDequeueTimeoutNs);
        if (status != android::NO_ERROR) {
            ALOGE("setDequeueTimeout failed");
            mProducer = nullptr;
            mProducerId = 0;
            return;
        }
    } else {
        mProducerId = 0;
    }
    mSlotAllocations.clear();
}

c2_status_t C2VdaBqBlockPool::cancelAllBuffers() {
    int32_t lastError = android::NO_ERROR;
    for (const auto& elem : mSlotAllocations) {
        int32_t slot = elem.first;
        sp<Fence> fence(Fence::NO_FENCE);
        native_handle_t* nh = nullptr;
        hidl_handle fenceHandle;
        android::conversion::wrapAs(&fenceHandle, &nh, *fence);
        int32_t status = mProducer->cancelBuffer(slot, fenceHandle);
        native_handle_delete(nh);
        if (status == android::NO_INIT) {
            // Producer may be disconnected during the loop of cancelBuffer (especially in the
            // destruction state). Break the loop and return immediately.
            mSlotAllocations.clear();
            return C2_NO_INIT;
        }
        if (status != android::NO_ERROR) {
            ALOGE("cancelBuffer failed at slot = %d, err: %d", slot, status);
            lastError = status;
        }
    }
    mSlotAllocations.clear();
    return asC2Error(lastError);
}