int PreviewRenderer::init() {
    int err = 0;
    ANativeWindow* anw = mSurface.get();

    err = native_window_api_connect(anw, NATIVE_WINDOW_API_CPU);
    if (err) goto fail;

    err = native_window_set_usage(
            anw, GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN);
    if (err) goto fail;

    err = native_window_set_buffer_count(anw, 3);
    if (err) goto fail;

    err = native_window_set_scaling_mode(
            anw, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (err) goto fail;

    err = native_window_set_buffers_geometry(
            anw, mWidth, mHeight, HAL_PIXEL_FORMAT_YV12);
    if (err) goto fail;

    err = native_window_set_buffers_transform(anw, 0);
    if (err) goto fail;

fail:
    return err;
}
void NativeWindowRenderer::queueExternalBuffer(ANativeWindow* anw,
    MediaBuffer* buffer, int width, int height) {
    native_window_set_buffers_geometry(anw, width, height,
            HAL_PIXEL_FORMAT_YV12);
    native_window_set_usage(anw, GRALLOC_USAGE_SW_WRITE_OFTEN);

#ifdef QCOM_HARDWARE
    native_window_set_buffer_count(anw, 3);
#endif

    ANativeWindowBuffer* anb;
    anw->dequeueBuffer(anw, &anb);
    CHECK(anb != NULL);

    sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
    CHECK(NO_ERROR == anw->lockBuffer(anw, buf->getNativeBuffer()));

    // Copy the buffer
    uint8_t* img = NULL;
    buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
    copyI420Buffer(buffer, img, width, height, buf->getStride());

#if PREVIEW_DEBUG
    FILE *fp1 = fopen("/sdcard/pre_test_yuv.raw", "ab");
    if(fp1 == NULL)
        LOGE("Errors file can not be created");
    else {
        fwrite(img, buf->getWidth() * buf->getHeight()* 3/2, 1, fp1);
        fclose(fp1);
    }
#endif

    buf->unlock();
    CHECK(NO_ERROR == anw->queueBuffer(anw, buf->getNativeBuffer()));
}
Exemple #3
0
int IOMXHWBuffer_SetBufferCount(void *window, unsigned int count )
{
    ANativeWindow *anw = (ANativeWindow *)window;
    status_t err;

    CHECK_ANW();

    LOGD( "IOMXHWBuffer_SetBufferCount: %p %u", anw, count );

    err = native_window_set_buffer_count( anw, count );
    CHECK_ERR();

    return 0;
}
Exemple #4
0
bool EncodeInputSurface::init(const char* inputFileName, uint32_t fourcc, int width, int height)
{
    m_width = width;
    m_height = height;

    int surfaceWidth = width;
    int surfaceHeight = height;
    static sp<SurfaceComposerClient> client = new SurfaceComposerClient();
    //create surface
    static sp<SurfaceControl> surfaceCtl = client->createSurface(String8("testsurface"),
        surfaceWidth, surfaceHeight, HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL, 0);
    DEBUG("create surface with size %dx%d, format: 0x%x", surfaceWidth, surfaceHeight, HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL);

    // configure surface
    SurfaceComposerClient::openGlobalTransaction();
    surfaceCtl->setLayer(100000);
    surfaceCtl->setPosition(100, 100);
    surfaceCtl->setSize(surfaceWidth, surfaceHeight);
    SurfaceComposerClient::closeGlobalTransaction();

    m_surface = surfaceCtl->getSurface();
    mNativeWindow = m_surface;

    int bufWidth = width;
    int bufHeight = height;
    int ret;

    INFO("set buffers geometry %dx%d\n", width, height);
    ret = native_window_set_buffers_dimensions(GET_ANATIVEWINDOW(mNativeWindow), width, height);
    CHECK_RET(ret, "native_window_set_buffers_dimensions");

    uint32_t format = HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL;
    INFO("set buffers format %x\n", format);
    ret = native_window_set_buffers_format(GET_ANATIVEWINDOW(mNativeWindow), format);
    CHECK_RET(ret, "native_window_set_buffers_format");

    INFO("set buffer usage\n");
    ret = native_window_set_usage(GET_ANATIVEWINDOW(mNativeWindow),
        GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_TEXTURE);
    CHECK_RET(ret, "native_window_set_usage");

    INFO("set buffer count %d\n", m_bufferCount);
    ret = native_window_set_buffer_count(GET_ANATIVEWINDOW(mNativeWindow), m_bufferCount);
    CHECK_RET(ret, "native_window_set_buffer_count");

    prepareInputBuffer();
    return true;
}
// This test probably doesn't belong here.
TEST_F(SurfaceTest, ScreenshotsOfProtectedBuffersSucceed) {
    sp<ANativeWindow> anw(mSurface);

    // Verify the screenshot works with no protected buffers.
    sp<CpuConsumer> consumer = new CpuConsumer(1);
    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    sp<IBinder> display(sf->getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(),
            64, 64, 0, 0x7fffffff, true));

    // Set the PROTECTED usage bit and verify that the screenshot fails.  Note
    // that we need to dequeue a buffer in order for it to actually get
    // allocated in SurfaceFlinger.
    ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(),
            GRALLOC_USAGE_PROTECTED));
    ASSERT_EQ(NO_ERROR, native_window_set_buffer_count(anw.get(), 3));
    ANativeWindowBuffer* buf = 0;

    status_t err = native_window_dequeue_buffer_and_wait(anw.get(), &buf);
    if (err) {
        // we could fail if GRALLOC_USAGE_PROTECTED is not supported.
        // that's okay as long as this is the reason for the failure.
        // try again without the GRALLOC_USAGE_PROTECTED bit.
        ASSERT_EQ(NO_ERROR, native_window_set_usage(anw.get(), 0));
        ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
                &buf));
        return;
    }
    ASSERT_EQ(NO_ERROR, anw->cancelBuffer(anw.get(), buf, -1));

    for (int i = 0; i < 4; i++) {
        // Loop to make sure SurfaceFlinger has retired a protected buffer.
        ASSERT_EQ(NO_ERROR, native_window_dequeue_buffer_and_wait(anw.get(),
                &buf));
        ASSERT_EQ(NO_ERROR, anw->queueBuffer(anw.get(), buf, -1));
    }
    ASSERT_EQ(NO_ERROR, sf->captureScreen(display, consumer->getBufferQueue(),
            64, 64, 0, 0x7fffffff, true));
}
Exemple #6
0
 void SurfaceUtils::setBufferCount(size_t bufferCount) {
     native_window_set_buffer_count(mWindow, bufferCount);
 }
status_t pushBlankBuffersToNativeWindow(ANativeWindow *nativeWindow /* nonnull */) {
    status_t err = NO_ERROR;
    ANativeWindowBuffer* anb = NULL;
    int numBufs = 0;
    int minUndequeuedBufs = 0;

    // We need to reconnect to the ANativeWindow as a CPU client to ensure that
    // no frames get dropped by SurfaceFlinger assuming that these are video
    // frames.
    err = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err), -err);
        return err;
    }

    err = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_CPU);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err);
        (void)native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
        return err;
    }

    err = setNativeWindowSizeFormatAndUsage(
            nativeWindow, 1, 1, HAL_PIXEL_FORMAT_RGBX_8888, 0, GRALLOC_USAGE_SW_WRITE_OFTEN);
    if (err != NO_ERROR) {
        goto error;
    }

    static_cast<Surface*>(nativeWindow)->getIGraphicBufferProducer()->allowAllocation(true);

    err = nativeWindow->query(nativeWindow,
            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: MIN_UNDEQUEUED_BUFFERS query "
                "failed: %s (%d)", strerror(-err), -err);
        goto error;
    }

    numBufs = minUndequeuedBufs + 1;
    err = native_window_set_buffer_count(nativeWindow, numBufs);
    if (err != NO_ERROR) {
        ALOGE("error pushing blank frames: set_buffer_count failed: %s (%d)", strerror(-err), -err);
        goto error;
    }

    // We push numBufs + 1 buffers to ensure that we've drawn into the same
    // buffer twice.  This should guarantee that the buffer has been displayed
    // on the screen and then been replaced, so an previous video frames are
    // guaranteed NOT to be currently displayed.
    for (int i = 0; i < numBufs + 1; i++) {
        err = native_window_dequeue_buffer_and_wait(nativeWindow, &anb);
        if (err != NO_ERROR) {
            ALOGE("error pushing blank frames: dequeueBuffer failed: %s (%d)",
                    strerror(-err), -err);
            break;
        }

        sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));

        // Fill the buffer with the a 1x1 checkerboard pattern ;)
        uint32_t *img = NULL;
        err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
        if (err != NO_ERROR) {
            ALOGE("error pushing blank frames: lock failed: %s (%d)", strerror(-err), -err);
            break;
        }

        *img = 0;

        err = buf->unlock();
        if (err != NO_ERROR) {
            ALOGE("error pushing blank frames: unlock failed: %s (%d)", strerror(-err), -err);
            break;
        }

        err = nativeWindow->queueBuffer(nativeWindow, buf->getNativeBuffer(), -1);
        if (err != NO_ERROR) {
            ALOGE("error pushing blank frames: queueBuffer failed: %s (%d)", strerror(-err), -err);
            break;
        }

        anb = NULL;
    }

error:

    if (anb != NULL) {
        nativeWindow->cancelBuffer(nativeWindow, anb, -1);
        anb = NULL;
    }

    // Clean up after success or error.
    status_t err2 = native_window_api_disconnect(nativeWindow, NATIVE_WINDOW_API_CPU);
    if (err2 != NO_ERROR) {
        ALOGE("error pushing blank frames: api_disconnect failed: %s (%d)", strerror(-err2), -err2);
        if (err == NO_ERROR) {
            err = err2;
        }
    }

    err2 = native_window_api_connect(nativeWindow, NATIVE_WINDOW_API_MEDIA);
    if (err2 != NO_ERROR) {
        ALOGE("error pushing blank frames: api_connect failed: %s (%d)", strerror(-err), -err);
        if (err == NO_ERROR) {
            err = err2;
        }
    }

    return err;
}
SoftwareRenderer::SoftwareRenderer(
        const sp<ANativeWindow> &nativeWindow, const sp<MetaData> &meta)
    : mConverter(NULL),
      mYUVMode(None),
      mNativeWindow(nativeWindow) {
    int32_t tmp;
    CHECK(meta->findInt32(kKeyColorFormat, &tmp));
    mColorFormat = (OMX_COLOR_FORMATTYPE)tmp;

    CHECK(meta->findInt32(kKeyWidth, &mWidth));
    CHECK(meta->findInt32(kKeyHeight, &mHeight));

    if (!meta->findRect(
                kKeyCropRect,
                &mCropLeft, &mCropTop, &mCropRight, &mCropBottom)) {
        mCropLeft = mCropTop = 0;
        mCropRight = mWidth - 1;
        mCropBottom = mHeight - 1;
    }

    mCropWidth = mCropRight - mCropLeft + 1;
    mCropHeight = mCropBottom - mCropTop + 1;

    int32_t rotationDegrees;
    if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
        rotationDegrees = 0;
    }

    int halFormat, minUndequeuedBufs = 0;
    size_t bufWidth, bufHeight;

    switch (mColorFormat) {
        case OMX_COLOR_FormatYUV420Planar:
        case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
        {
            halFormat = HAL_PIXEL_FORMAT_YV12;
            bufWidth = (mCropWidth + 1) & ~1;
            bufHeight = (mCropHeight + 1) & ~1;
            break;
        }

        default:
            halFormat = HAL_PIXEL_FORMAT_RGB_565;
            bufWidth = mCropWidth;
            bufHeight = mCropHeight;

            mConverter = new ColorConverter(
                    mColorFormat, OMX_COLOR_Format16bitRGB565);
            CHECK(mConverter->isValid());
            break;
    }

    CHECK(mNativeWindow != NULL);
    CHECK(mCropWidth > 0);
    CHECK(mCropHeight > 0);
    CHECK(mConverter == NULL || mConverter->isValid());
    CHECK_EQ(0,
            mNativeWindow->query(
            mNativeWindow.get(),
            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs));
    CHECK_EQ(0,
            native_window_set_buffer_count(
            mNativeWindow.get(),
            minUndequeuedBufs + 1));
    CHECK_EQ(0,
            native_window_set_usage(
            mNativeWindow.get(),
            GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
            | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));

    CHECK_EQ(0,
            native_window_set_scaling_mode(
            mNativeWindow.get(),
            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW));

    // Width must be multiple of 32???
    CHECK_EQ(0, native_window_set_buffers_geometry(
                mNativeWindow.get(),
                bufWidth,
                bufHeight,
                halFormat));

    uint32_t transform;
    switch (rotationDegrees) {
        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;
        default: transform = 0; break;
    }

    if (transform) {
        CHECK_EQ(0, native_window_set_buffers_transform(
                    mNativeWindow.get(), transform));
    }
}
status_t Camera3OutputStream::configureQueueLocked() {
    status_t res;

    mTraceFirstBuffer = true;
    if ((res = Camera3IOStreamBase::configureQueueLocked()) != OK) {
        return res;
    }

    ALOG_ASSERT(mConsumer != 0, "mConsumer should never be NULL");

    // Configure consumer-side ANativeWindow interface
    res = native_window_api_connect(mConsumer.get(),
            NATIVE_WINDOW_API_CAMERA);
    if (res != OK) {
        ALOGE("%s: Unable to connect to native window for stream %d",
                __FUNCTION__, mId);
        return res;
    }

    res = native_window_set_usage(mConsumer.get(), camera3_stream::usage);
    if (res != OK) {
        ALOGE("%s: Unable to configure usage %08x for stream %d",
                __FUNCTION__, camera3_stream::usage, mId);
        return res;
    }

    res = native_window_set_scaling_mode(mConsumer.get(),
            NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
    if (res != OK) {
        ALOGE("%s: Unable to configure stream scaling: %s (%d)",
                __FUNCTION__, strerror(-res), res);
        return res;
    }

    if (mMaxSize == 0) {
        // For buffers of known size
        res = native_window_set_buffers_dimensions(mConsumer.get(),
                camera3_stream::width, camera3_stream::height);
    } else {
        // For buffers with bounded size
        res = native_window_set_buffers_dimensions(mConsumer.get(),
                mMaxSize, 1);
    }
    if (res != OK) {
        ALOGE("%s: Unable to configure stream buffer dimensions"
                " %d x %d (maxSize %zu) for stream %d",
                __FUNCTION__, camera3_stream::width, camera3_stream::height,
                mMaxSize, mId);
        return res;
    }
    res = native_window_set_buffers_format(mConsumer.get(),
            camera3_stream::format);
    if (res != OK) {
        ALOGE("%s: Unable to configure stream buffer format %#x for stream %d",
                __FUNCTION__, camera3_stream::format, mId);
        return res;
    }

    int maxConsumerBuffers;
    res = mConsumer->query(mConsumer.get(),
            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
    if (res != OK) {
        ALOGE("%s: Unable to query consumer undequeued"
                " buffer count for stream %d", __FUNCTION__, mId);
        return res;
    }

    ALOGV("%s: Consumer wants %d buffers, HAL wants %d", __FUNCTION__,
            maxConsumerBuffers, camera3_stream::max_buffers);
    if (camera3_stream::max_buffers == 0) {
        ALOGE("%s: Camera HAL requested max_buffer count: %d, requires at least 1",
                __FUNCTION__, camera3_stream::max_buffers);
        return INVALID_OPERATION;
    }

    mTotalBufferCount = maxConsumerBuffers + camera3_stream::max_buffers;
    mHandoutTotalBufferCount = 0;
    mFrameCount = 0;
    mLastTimestamp = 0;

    res = native_window_set_buffer_count(mConsumer.get(),
            mTotalBufferCount);
    if (res != OK) {
        ALOGE("%s: Unable to set buffer count for stream %d",
                __FUNCTION__, mId);
        return res;
    }

    res = native_window_set_buffers_transform(mConsumer.get(),
            mTransform);
    if (res != OK) {
        ALOGE("%s: Unable to configure stream transform to %x: %s (%d)",
                __FUNCTION__, mTransform, strerror(-res), res);
    }

    return OK;
}
status_t StreamAdapter::connectToDevice(camera2_device_t *d,
        uint32_t width, uint32_t height, int format) {
    if (mState != UNINITIALIZED) return INVALID_OPERATION;
    if (d == NULL) {
        ALOGE("%s: Null device passed to stream adapter", __FUNCTION__);
        return BAD_VALUE;
    }

    status_t res;

    mWidth = width;
    mHeight = height;
    mFormat = format;

    // Allocate device-side stream interface

    uint32_t id;
    uint32_t formatActual; // ignored
    uint32_t usage;
    uint32_t maxBuffers = 2;
    res = d->ops->allocate_stream(d,
            mWidth, mHeight, mFormat, getStreamOps(),
            &id, &formatActual, &usage, &maxBuffers);
    if (res != OK) {
        ALOGE("%s: Device stream allocation failed: %s (%d)",
                __FUNCTION__, strerror(-res), res);
        mState = UNINITIALIZED;
        return res;
    }
    mDevice = d;

    mId = id;
    mUsage = usage;
    mMaxProducerBuffers = maxBuffers;

    // Configure consumer-side ANativeWindow interface

    res = native_window_api_connect(mConsumerInterface.get(),
            NATIVE_WINDOW_API_CAMERA);
    if (res != OK) {
        ALOGE("%s: Unable to connect to native window for stream %d",
                __FUNCTION__, mId);
        mState = ALLOCATED;
        return res;
    }

    res = native_window_set_usage(mConsumerInterface.get(), mUsage);
    if (res != OK) {
        ALOGE("%s: Unable to configure usage %08x for stream %d",
                __FUNCTION__, mUsage, mId);
        mState = CONNECTED;
        return res;
    }

    res = native_window_set_buffers_geometry(mConsumerInterface.get(),
            mWidth, mHeight, mFormat);
    if (res != OK) {
        ALOGE("%s: Unable to configure buffer geometry"
                " %d x %d, format 0x%x for stream %d",
                __FUNCTION__, mWidth, mHeight, mFormat, mId);
        mState = CONNECTED;
        return res;
    }

    int maxConsumerBuffers;
    res = mConsumerInterface->query(mConsumerInterface.get(),
            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
    if (res != OK) {
        ALOGE("%s: Unable to query consumer undequeued"
                " buffer count for stream %d", __FUNCTION__, mId);
        mState = CONNECTED;
        return res;
    }
    mMaxConsumerBuffers = maxConsumerBuffers;

    ALOGV("%s: Producer wants %d buffers, consumer wants %d", __FUNCTION__,
            mMaxProducerBuffers, mMaxConsumerBuffers);

    int totalBuffers = mMaxConsumerBuffers + mMaxProducerBuffers;

    res = native_window_set_buffer_count(mConsumerInterface.get(),
            totalBuffers);
    if (res != OK) {
        ALOGE("%s: Unable to set buffer count for stream %d",
                __FUNCTION__, mId);
        mState = CONNECTED;
        return res;
    }

    // Register allocated buffers with HAL device
    buffer_handle_t *buffers = new buffer_handle_t[totalBuffers];
    ANativeWindowBuffer **anwBuffers = new ANativeWindowBuffer*[totalBuffers];
    int bufferIdx = 0;
    for (; bufferIdx < totalBuffers; bufferIdx++) {
        res = native_window_dequeue_buffer_and_wait(mConsumerInterface.get(),
                &anwBuffers[bufferIdx]);
        if (res != OK) {
            ALOGE("%s: Unable to dequeue buffer %d for initial registration for"
                    "stream %d", __FUNCTION__, bufferIdx, mId);
            mState = CONNECTED;
            goto cleanUpBuffers;
        }
        buffers[bufferIdx] = anwBuffers[bufferIdx]->handle;
    }

    res = mDevice->ops->register_stream_buffers(mDevice,
            mId,
            totalBuffers,
            buffers);
    if (res != OK) {
        ALOGE("%s: Unable to register buffers with HAL device for stream %d",
                __FUNCTION__, mId);
        mState = CONNECTED;
    } else {
        mState = ACTIVE;
    }

cleanUpBuffers:
    for (int i = 0; i < bufferIdx; i++) {
        res = mConsumerInterface->cancelBuffer(mConsumerInterface.get(),
                anwBuffers[i], -1);
    }
    delete anwBuffers;
    delete buffers;

    return res;
}
Exemple #11
0
int main(int argc, char** argv)
{
    DecodeInput *input;
    int32_t fd = -1;
    int32_t i = 0;
    int32_t ioctlRet = -1;
    YamiMediaCodec::CalcFps calcFps;

    renderMode = 3; // set default render mode to 3

    yamiTraceInit();
#if __ENABLE_V4L2_GLX__
    XInitThreads();
#endif

#if __ENABLE_V4L2_OPS__
    // FIXME, use libv4l2codec_hw.so instead
    if (!loadV4l2CodecDevice("libyami_v4l2.so")) {
        ERROR("fail to init v4l2codec device with __ENABLE_V4L2_OPS__\n");
        return -1;
    }
#endif

    if (!process_cmdline(argc, argv))
        return -1;

    if (!inputFileName) {
        ERROR("no input media file specified\n");
        return -1;
    }
    INFO("input file: %s, renderMode: %d", inputFileName, renderMode);

    if (!dumpOutputName)
        dumpOutputName = strdup ("./");

#if !__ENABLE_V4L2_GLX__
    switch (renderMode) {
    case 0:
        memoryType = VIDEO_DATA_MEMORY_TYPE_RAW_COPY;
        memoryTypeStr = typeStrRawData;
    break;
    case 3:
        memoryType = VIDEO_DATA_MEMORY_TYPE_DRM_NAME;
        memoryTypeStr = typeStrDrmName;
    break;
    case 4:
        memoryType = VIDEO_DATA_MEMORY_TYPE_DMA_BUF;
        memoryTypeStr = typeStrDmaBuf;
    break;
    default:
        ASSERT(0 && "unsupported render mode, -m [0,3,4] are supported");
    break;
    }
#endif

    input = DecodeInput::create(inputFileName);
    if (input==NULL) {
        ERROR("fail to init input stream\n");
        return -1;
    }

    renderFrameCount = 0;
    calcFps.setAnchor();
    // open device
    fd = SIMULATE_V4L2_OP(Open)("decoder", 0);
    ASSERT(fd!=-1);

#ifdef ANDROID
#elif __ENABLE_V4L2_GLX__
    x11Display = XOpenDisplay(NULL);
    ASSERT(x11Display);
    ioctlRet = SIMULATE_V4L2_OP(SetXDisplay)(fd, x11Display);
#endif
    // set output frame memory type
#if __ENABLE_V4L2_OPS__
    SIMULATE_V4L2_OP(SetParameter)(fd, "frame-memory-type", memoryTypeStr);
#elif !__ENABLE_V4L2_GLX__
    SIMULATE_V4L2_OP(FrameMemoryType)(fd, memoryType);
#endif

    // query hw capability
    struct v4l2_capability caps;
    memset(&caps, 0, sizeof(caps));
    caps.capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE | V4L2_CAP_STREAMING;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_QUERYCAP, &caps);
    ASSERT(ioctlRet != -1);

    // set input/output data format
    uint32_t codecFormat = v4l2PixelFormatFromMime(input->getMimeType());
    if (!codecFormat) {
        ERROR("unsupported mimetype, %s", input->getMimeType());
        return -1;
    }

    struct v4l2_format format;
    memset(&format, 0, sizeof(format));
    format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    format.fmt.pix_mp.pixelformat = codecFormat;
    format.fmt.pix_mp.num_planes = 1;
    format.fmt.pix_mp.plane_fmt[0].sizeimage = k_maxInputBufferSize;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_S_FMT, &format);
    ASSERT(ioctlRet != -1);

    // set preferred output format
    memset(&format, 0, sizeof(format));
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    uint8_t* data = (uint8_t*)input->getCodecData().data();
    uint32_t size = input->getCodecData().size();
    //save codecdata, size+data, the type of format.fmt.raw_data is __u8[200]
    //we must make sure enough space (>=sizeof(uint32_t) + size) to store codecdata
    memcpy(format.fmt.raw_data, &size, sizeof(uint32_t));
    if(sizeof(format.fmt.raw_data) >= size + sizeof(uint32_t))
        memcpy(format.fmt.raw_data + sizeof(uint32_t), data, size);
    else {
        ERROR("No enough space to store codec data");
        return -1;
    }
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_S_FMT, &format);
    ASSERT(ioctlRet != -1);

    // input port starts as early as possible to decide output frame format
    __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_STREAMON, &type);
    ASSERT(ioctlRet != -1);

    // setup input buffers
    struct v4l2_requestbuffers reqbufs;
    memset(&reqbufs, 0, sizeof(reqbufs));
    reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    reqbufs.memory = V4L2_MEMORY_MMAP;
    reqbufs.count = 2;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_REQBUFS, &reqbufs);
    ASSERT(ioctlRet != -1);
    ASSERT(reqbufs.count>0);
    inputQueueCapacity = reqbufs.count;
    inputFrames.resize(inputQueueCapacity);

    for (i=0; i<inputQueueCapacity; i++) {
        struct v4l2_plane planes[k_inputPlaneCount];
        struct v4l2_buffer buffer;
        memset(&buffer, 0, sizeof(buffer));
        memset(planes, 0, sizeof(planes));
        buffer.index = i;
        buffer.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
        buffer.memory = V4L2_MEMORY_MMAP;
        buffer.m.planes = planes;
        buffer.length = k_inputPlaneCount;
        ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_QUERYBUF, &buffer);
        ASSERT(ioctlRet != -1);

        // length and mem_offset should be filled by VIDIOC_QUERYBUF above
        void* address = SIMULATE_V4L2_OP(Mmap)(NULL,
                                      buffer.m.planes[0].length,
                                      PROT_READ | PROT_WRITE,
                                      MAP_SHARED, fd,
                                      buffer.m.planes[0].m.mem_offset);
        ASSERT(address);
        inputFrames[i] = static_cast<uint8_t*>(address);
        DEBUG("inputFrames[%d] = %p", i, inputFrames[i]);
    }

    // feed input frames first
    for (i=0; i<inputQueueCapacity; i++) {
        if (!feedOneInputFrame(input, fd, i)) {
            break;
        }
    }

    // query video resolution
    memset(&format, 0, sizeof(format));
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    while (1) {
        if (SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_G_FMT, &format) != 0) {
            if (errno != EINVAL) {
                // EINVAL means we haven't seen sufficient stream to decode the format.
                INFO("ioctl() failed: VIDIOC_G_FMT, haven't get video resolution during start yet, waiting");
            }
        } else {
            break;
        }
        usleep(50);
    }
    outputPlaneCount = format.fmt.pix_mp.num_planes;
    ASSERT(outputPlaneCount == 2);
    videoWidth = format.fmt.pix_mp.width;
    videoHeight = format.fmt.pix_mp.height;
    ASSERT(videoWidth && videoHeight);

#ifdef ANDROID
    __u32 pixelformat = format.fmt.pix_mp.pixelformat;
    if (!createNativeWindow(pixelformat)) {
        fprintf(stderr, "create native window error\n");
        return -1;
    }

    int minUndequeuedBuffs = 0;
    status_t err = mNativeWindow->query(mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBuffs);
    if (err != 0) {
        fprintf(stderr, "query native window min undequeued buffers error\n");
        return err;
    }
#endif

    // setup output buffers
    // Number of output buffers we need.
    struct v4l2_control ctrl;
    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_G_CTRL, &ctrl);
#ifndef ANDROID
    uint32_t minOutputFrameCount = ctrl.value + k_extraOutputFrameCount;
#else
    uint32_t minOutputFrameCount = ctrl.value + k_extraOutputFrameCount + minUndequeuedBuffs;
#endif

    memset(&reqbufs, 0, sizeof(reqbufs));
    reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    reqbufs.memory = V4L2_MEMORY_MMAP;
    reqbufs.count = minOutputFrameCount;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_REQBUFS, &reqbufs);
    ASSERT(ioctlRet != -1);
    ASSERT(reqbufs.count>0);
    outputQueueCapacity = reqbufs.count;

#ifdef ANDROID
#elif __ENABLE_V4L2_GLX__
    x11Window = XCreateSimpleWindow(x11Display, DefaultRootWindow(x11Display)
        , 0, 0, videoWidth, videoHeight, 0, 0
        , WhitePixel(x11Display, 0));
    XMapWindow(x11Display, x11Window);
    pixmaps.resize(outputQueueCapacity);
    glxPixmaps.resize(outputQueueCapacity);
    textureIds.resize(outputQueueCapacity);

    if (!glxContext) {
        glxContext = glxInit(x11Display, x11Window);
    }
    ASSERT(glxContext);

    glGenTextures(outputQueueCapacity, &textureIds[0] );
    for (i=0; i<outputQueueCapacity; i++) {
        int ret = createPixmapForTexture(glxContext, textureIds[i], videoWidth, videoHeight, &pixmaps[i], &glxPixmaps[i]);
        DEBUG("textureIds[%d]: 0x%x, pixmaps[%d]=0x%lx, glxPixmaps[%d]: 0x%lx", i, textureIds[i], i, pixmaps[i], i, glxPixmaps[i]);
        ASSERT(ret == 0);
        ret = SIMULATE_V4L2_OP(UsePixmap)(fd, i, pixmaps[i]);
        ASSERT(ret == 0);
    }
#else
    if (IS_RAW_DATA()) {
        rawOutputFrames.resize(outputQueueCapacity);
        for (i=0; i<outputQueueCapacity; i++) {
            struct v4l2_plane planes[k_maxOutputPlaneCount];
            struct v4l2_buffer buffer;
            memset(&buffer, 0, sizeof(buffer));
            memset(planes, 0, sizeof(planes));
            buffer.index = i;
            buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
            buffer.memory = V4L2_MEMORY_MMAP;
            buffer.m.planes = planes;
            buffer.length = outputPlaneCount;
            ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_QUERYBUF, &buffer);
            ASSERT(ioctlRet != -1);

            rawOutputFrames[i].width = format.fmt.pix_mp.width;
            rawOutputFrames[i].height = format.fmt.pix_mp.height;
            rawOutputFrames[i].fourcc = format.fmt.pix_mp.pixelformat;

            for (int j=0; j<outputPlaneCount; j++) {
                // length and mem_offset are filled by VIDIOC_QUERYBUF above
                void* address = SIMULATE_V4L2_OP(Mmap)(NULL,
                                              buffer.m.planes[j].length,
                                              PROT_READ | PROT_WRITE,
                                              MAP_SHARED, fd,
                                              buffer.m.planes[j].m.mem_offset);
                ASSERT(address);
                if (j == 0) {
                    rawOutputFrames[i].data = static_cast<uint8_t*>(address);
                    rawOutputFrames[i].offset[0] = 0;
                } else {
                    rawOutputFrames[i].offset[j] = static_cast<uint8_t*>(address) - rawOutputFrames[i].data;
                }

                rawOutputFrames[i].pitch[j] = format.fmt.pix_mp.plane_fmt[j].bytesperline;
            }
        }
    } else if (IS_DMA_BUF() || IS_DRM_NAME()) {
        // setup all textures and eglImages
        eglImages.resize(outputQueueCapacity);
        textureIds.resize(outputQueueCapacity);

        if (!eglContext)
            eglContext = eglInit(x11Display, x11Window, 0 /*VA_FOURCC_RGBA*/, IS_DMA_BUF());

        glGenTextures(outputQueueCapacity, &textureIds[0] );
        for (i=0; i<outputQueueCapacity; i++) {
             int ret = 0;
             ret = SIMULATE_V4L2_OP(UseEglImage)(fd, eglContext->eglContext.display, eglContext->eglContext.context, i, &eglImages[i]);
             ASSERT(ret == 0);

             GLenum target = GL_TEXTURE_2D;
             if (IS_DMA_BUF())
                 target = GL_TEXTURE_EXTERNAL_OES;
             glBindTexture(target, textureIds[i]);
             imageTargetTexture2D(target, eglImages[i]);

             glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
             DEBUG("textureIds[%d]: 0x%x, eglImages[%d]: 0x%p", i, textureIds[i], i, eglImages[i]);
        }
    }
#endif

#ifndef ANDROID
    // feed output frames first
    for (i=0; i<outputQueueCapacity; i++) {
        if (!takeOneOutputFrame(fd, i)) {
            ASSERT(0);
        }
    }
#else
    struct v4l2_buffer buffer;

    err = native_window_set_buffer_count(mNativeWindow.get(), outputQueueCapacity);
    if (err != 0) {
        fprintf(stderr, "native_window_set_buffer_count failed: %s (%d)", strerror(-err), -err);
        return -1;
    }

    //queue buffs
    for (uint32_t i = 0; i < outputQueueCapacity; i++) {
        ANativeWindowBuffer* pbuf = NULL;
        memset(&buffer, 0, sizeof(buffer));

        err = native_window_dequeue_buffer_and_wait(mNativeWindow.get(), &pbuf);
        if (err != 0) {
            fprintf(stderr, "dequeueBuffer failed: %s (%d)\n", strerror(-err), -err);
            return -1;
        }

        buffer.m.userptr = (unsigned long)pbuf;
        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        buffer.index = i;

        ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_QBUF, &buffer);
        ASSERT(ioctlRet != -1);
        mWindBuff.push_back(pbuf);
    }

    for (uint32_t i = 0; i < minUndequeuedBuffs; i++) {
        memset(&buffer, 0, sizeof(buffer));
        buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

        ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_DQBUF, &buffer);
        ASSERT(ioctlRet != -1);

        err = mNativeWindow->cancelBuffer(mNativeWindow.get(), mWindBuff[buffer.index], -1);
        if (err) {
            fprintf(stderr, "queue empty window buffer error\n");
            return -1;
        }
    }
#endif

    // output port starts as late as possible to adopt user provide output buffer
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_STREAMON, &type);
    ASSERT(ioctlRet != -1);

    bool event_pending=true; // try to get video resolution.
    int dqCountAfterEOS = 0;
    do {
        if (event_pending) {
            handleResolutionChange(fd);
        }

        takeOneOutputFrame(fd);
        if (!feedOneInputFrame(input, fd)) {
            if (stagingBufferInDevice == 0)
                break;
            dqCountAfterEOS++;
        }
        if (dqCountAfterEOS == inputQueueCapacity)  // input drain
            break;
    } while (SIMULATE_V4L2_OP(Poll)(fd, true, &event_pending) == 0);

    // drain output buffer
    int retry = 3;
    while (takeOneOutputFrame(fd) || (--retry)>0) { // output drain
        usleep(10000);
    }

    calcFps.fps(renderFrameCount);
    // SIMULATE_V4L2_OP(Munmap)(void* addr, size_t length)
    possibleWait(input->getMimeType());

    // release queued input/output buffer
    memset(&reqbufs, 0, sizeof(reqbufs));
    reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    reqbufs.memory = V4L2_MEMORY_MMAP;
    reqbufs.count = 0;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_REQBUFS, &reqbufs);
    ASSERT(ioctlRet != -1);

    memset(&reqbufs, 0, sizeof(reqbufs));
    reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    reqbufs.memory = V4L2_MEMORY_MMAP;
    reqbufs.count = 0;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_REQBUFS, &reqbufs);
    ASSERT(ioctlRet != -1);

    // stop input port
    type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_STREAMOFF, &type);
    ASSERT(ioctlRet != -1);

    // stop output port
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    ioctlRet = SIMULATE_V4L2_OP(Ioctl)(fd, VIDIOC_STREAMOFF, &type);
    ASSERT(ioctlRet != -1);

#ifndef ANDROID
    if(textureIds.size())
        glDeleteTextures(textureIds.size(), &textureIds[0]);
    ASSERT(glGetError() == GL_NO_ERROR);
#endif

#ifdef ANDROID
    //TODO, some resources need to destroy?
#elif __ENABLE_V4L2_GLX__
    glxRelease(glxContext, &pixmaps[0], &glxPixmaps[0], pixmaps.size());
#else
    for (i=0; i<eglImages.size(); i++) {
        destroyImage(eglContext->eglContext.display, eglImages[i]);
    }
    /*
    there is still randomly fail in mesa; no good idea for it. seems mesa bug
    0  0x00007ffff079c343 in _mesa_symbol_table_dtor () from /usr/lib/x86_64-linux-gnu/libdricore9.2.1.so.1
    1  0x00007ffff073c55d in glsl_symbol_table::~glsl_symbol_table() () from /usr/lib/x86_64-linux-gnu/libdricore9.2.1.so.1
    2  0x00007ffff072a4d5 in ?? () from /usr/lib/x86_64-linux-gnu/libdricore9.2.1.so.1
    3  0x00007ffff072a4bd in ?? () from /usr/lib/x86_64-linux-gnu/libdricore9.2.1.so.1
    4  0x00007ffff064b48f in _mesa_reference_shader () from /usr/lib/x86_64-linux-gnu/libdricore9.2.1.so.1
    5  0x00007ffff0649397 in ?? () from /usr/lib/x86_64-linux-gnu/libdricore9.2.1.so.1
    6  0x000000000040624d in releaseShader (program=0x77cd90) at ./egl/gles2_help.c:158
    7  eglRelease (context=0x615920) at ./egl/gles2_help.c:310
    8  0x0000000000402ca8 in main (argc=<optimized out>, argv=<optimized out>) at v4l2decode.cpp:531
    */
    if (eglContext)
        eglRelease(eglContext);
#endif

    // close device
    ioctlRet = SIMULATE_V4L2_OP(Close)(fd);
    ASSERT(ioctlRet != -1);

    if(input)
        delete input;

    if (outfp)
        fclose(outfp);

    if (dumpOutputName)
        free(dumpOutputName);

#if __ENABLE_V4L2_GLX__
    if (x11Display && x11Window)
        XDestroyWindow(x11Display, x11Window);
    if (x11Display)
        XCloseDisplay(x11Display);
#endif

    fprintf(stdout, "decode done\n");
}
Exemple #12
0
bool_e anativewindow_allocate(anativewindow_t *anw,
                              uint32_t buffer_width,
                              uint32_t buffer_height,
                              int32_t numBuffers,
                              int32_t format,
                              bool flipH)
{
    if (format != ANW_NV12_FORMAT)
        return false_e;

    if (anw)
    {
        status_t status = 0;

        anw->m_format = format;
        anw->m_usage = GRALLOC_USAGE_HW_TEXTURE |
                       GRALLOC_USAGE_HW_RENDER |
                       GRALLOC_USAGE_SW_READ_OFTEN | // Non-cached ? If you use RARELY it will complain
                       GRALLOC_USAGE_SW_WRITE_NEVER; // can't access from UVA
        anw->m_numBuffers = numBuffers;
        anw->m_width = buffer_width;
        anw->m_height = buffer_height;
        anw->m_native_buffers = (ANativeWindowBuffer **)calloc(numBuffers, sizeof(ANativeWindowBuffer *));
        DVP_PRINT(DVP_ZONE_VIDEO, "Created %u pointers in native array %p\n", numBuffers, anw->m_native_buffers);
        if (anw->m_native_buffers == NULL) {
            return false_e;
        }

        // connect to the native window API
        status = native_window_api_connect(anw->m_window.get(), NATIVE_WINDOW_API_CAMERA);
        ANW_ERROR(status,"CONNECT");

        // set the scaling mode of the windows
        status = native_window_set_scaling_mode(anw->m_window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
        ANW_ERROR(status,"SCALING");

#if defined(BLAZE_TABLET) && defined(ICS)
        // set the layout of the buffer
        status = native_window_set_buffers_layout(anw->m_window.get(), NATIVE_WINDOW_BUFFERS_LAYOUT_PROGRESSIVE); // progressive
        ANW_ERROR(status,"LAYOUT");
#endif

        // set the format of the buffer
        status = native_window_set_buffers_format(anw->m_window.get(), anw->m_format);
        ANW_ERROR(status,"FORMAT");

        // setup the dimensions
        status = native_window_set_buffers_dimensions(anw->m_window.get(), anw->m_width, anw->m_height);
        ANW_ERROR(status,"DIM");

        if (flipH) {
            // set the horizontal flip
            status = native_window_set_buffers_transform(anw->m_window.get(), NATIVE_WINDOW_TRANSFORM_FLIP_H);
            ANW_ERROR(status,"TRANSFORM FLIP HORIZONTAL");
        }

        // set the usage of the GRALLOC buffers
        status = native_window_set_usage(anw->m_window.get(), anw->m_usage);
        if (status < 0 ) {
            DVP_PRINT(DVP_ZONE_ERROR, "%s[%u] USAGE status = %d (0x%08x)\n", __FUNCTION__, __LINE__, status, anw->m_usage);
        }

        // set the number of buffers required.
        status = native_window_set_buffer_count(anw->m_window.get(), anw->m_numBuffers);
        ANW_ERROR(status,"COUNT");

        // get the number of dequeueable buffers
        status = anw->m_window->query(anw->m_window.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, (int*)&anw->m_nonqueue);
        ANW_ERROR(status,"QUERY NONQUEUE");

        for (uint32_t n = 0; n < anw->m_numBuffers; n++)
        {
            status = anw->m_window->dequeueBuffer(anw->m_window.get(), &anw->m_native_buffers[n]);
            if (status != 0) {
                DVP_PRINT(DVP_ZONE_ERROR, "%s[%u] %s status = %d\n", __FUNCTION__, __LINE__, "Failed window buffer dequeue!", status); \
                return false_e;
            }
            if (anw->m_native_buffers[n])
            {
                native_handle_t *hdl = (native_handle_t *)anw->m_native_buffers[n]->handle;
                hdl = hdl; // warnings
                DVP_PRINT(DVP_ZONE_VIDEO, "ANativeBuffer %p => dim %dx%d stride %d usage %d format %d handle %p numFds=%u\n",
                         anw->m_native_buffers[n],
                         anw->m_native_buffers[n]->width,
                         anw->m_native_buffers[n]->height,
                         anw->m_native_buffers[n]->stride,
                         anw->m_native_buffers[n]->usage,
                         anw->m_native_buffers[n]->format,
                         anw->m_native_buffers[n]->handle,
                         hdl->numFds);
            }
        }
        return true_e;
    }
    return false_e;
}
status_t Camera3StreamSplitter::addOutputLocked(const sp<Surface>& outputQueue) {
    ATRACE_CALL();
    if (outputQueue == nullptr) {
        SP_LOGE("addOutput: outputQueue must not be NULL");
        return BAD_VALUE;
    }

    sp<IGraphicBufferProducer> gbp = outputQueue->getIGraphicBufferProducer();
    // Connect to the buffer producer
    sp<OutputListener> listener(new OutputListener(this, gbp));
    IInterface::asBinder(gbp)->linkToDeath(listener);
    status_t res = outputQueue->connect(NATIVE_WINDOW_API_CAMERA, listener);
    if (res != NO_ERROR) {
        SP_LOGE("addOutput: failed to connect (%d)", res);
        return res;
    }

    // Query consumer side buffer count, and update overall buffer count
    int maxConsumerBuffers = 0;
    res = static_cast<ANativeWindow*>(outputQueue.get())->query(
            outputQueue.get(),
            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &maxConsumerBuffers);
    if (res != OK) {
        SP_LOGE("%s: Unable to query consumer undequeued buffer count"
              " for surface", __FUNCTION__);
        return res;
    }

    SP_LOGV("%s: Consumer wants %d buffers, Producer wants %zu", __FUNCTION__,
            maxConsumerBuffers, mMaxHalBuffers);
    size_t totalBufferCount = maxConsumerBuffers + mMaxHalBuffers;
    res = native_window_set_buffer_count(outputQueue.get(),
            totalBufferCount);
    if (res != OK) {
        SP_LOGE("%s: Unable to set buffer count for surface %p",
                __FUNCTION__, outputQueue.get());
        return res;
    }

    // Set dequeueBuffer/attachBuffer timeout if the consumer is not hw composer or hw texture.
    // We need skip these cases as timeout will disable the non-blocking (async) mode.
    uint64_t usage = 0;
    res = native_window_get_consumer_usage(static_cast<ANativeWindow*>(outputQueue.get()), &usage);
    if (!(usage & (GRALLOC_USAGE_HW_COMPOSER | GRALLOC_USAGE_HW_TEXTURE))) {
        outputQueue->setDequeueTimeout(kDequeueBufferTimeout);
    }

    res = gbp->allowAllocation(false);
    if (res != OK) {
        SP_LOGE("%s: Failed to turn off allocation for outputQueue", __FUNCTION__);
        return res;
    }

    // Add new entry into mOutputs
    mOutputs.push_back(gbp);
    mNotifiers[gbp] = listener;
    mOutputSlots[gbp] = std::make_unique<OutputSlots>(totalBufferCount);

    mMaxConsumerBuffers += maxConsumerBuffers;
    return NO_ERROR;
}