Tegra_Multimedia API 例程学习之7--07_video_convert

概述

这个例子展示了:

  • 如何使用libv4l2 时行视频的缩放和颜色格式的转化

编译和运行

编译和以前一样,进入目录直接make即可。
运行程序

$ ./video_convert <in-file> <in-width> <in-height> <in-format> <out-file> <out-width> <out-height> <out-format> [OPTIONS]

例如:

$ ./video_convert ../../data/Picture/nvidia-logo.yuv 1920 1080 YUV420M test.yuv 1920 1080 YUYV

如果没有yuv文件,可以使用以下命令生成一个

 $ cd $HOME/multimedia_api/samples/06_jpeg_decode/
 $ ./jpeg_decode num_files 1 ../../data/Picture/nvidia-logo.jpg ../../data/Picture/nvidia-logo.yuv

流程

下面的流程图显示了程序的流程。
在这里插入图片描述
视频转换可以从YV12到RGB,或者NV12到YV12等。NVIDIA Tegra对于输入和输出提供了planepitch linear memory(这个类型不知道是什么类型,反正是一种内存格式)和block linear memory。输入和输出格式可以不匹配,例如,YV12转RGB,设置YV12为以下参数

ret = ctx.conv0->setOutputPlaneFormat(ctx.in_pixfmt, \
            ctx.in_width, \
            ctx.in_height, \
            V4L2_NV_BUFFER_LAYOUT_PITCH);

示例中的上述调用设置libv4l2输出平面,即接收从文件读取的输入yuv数据,作为pitch线性内存格式。例如,对于yv12到rgb的转换,请将format参数设置为rgb。

ret = ctx.conv0->setCapturePlaneFormat(ctx.out_pixfmt, \
            ctx.out_width, \
            ctx.out_height, \
            V4L2_NV_BUFFER_LAYOUT_PITCH);

示例中的上述调用设置libv4l2捕获平面。在这种情况下,它指定libv4l2捕获平面必须具有最常见的pitch线性内存格式。(这里不明白,为什么要是pitch线性内存格式?)因此,如果命令行指示捕获平面为块线性(通过–input-nvbl选项,应用和程序的选项),则示例通过初始化两个 NvVideoConverter 对象来适应此请求,其中:

  • 第一个对象将pitch线性存储器格式转换为block(这里感觉理解为阻塞更合适)存储器格式。由于输入缓冲区是 block-linear,因此该对象被有效地阻塞,并且 block-linear input buffers被转移到第二个转换器。
 ret = ctx.conv0->setCapturePlaneFormat(ctx.in_pixfmt, \
          ctx.in_width, \
          ctx.in_height, \
          V4L2_NV_BUFFER_LAYOUT_BLOCKLINEAR);
  • 第二个对象(主对象)将block-linear存储器格式转换为pitch-linear存储器格式。这允许将pitch-linear输出缓冲区转储到文件中。
ret = ctx.conv1->setOutputPlaneFormat(ctx.in_pixfmt, \
        ctx.in_width, \
        ctx.in_height, \
        V4L2_NV_BUFFER_LAYOUT_BLOCKLINEAR);
ret = ctx.conv1->setCapturePlaneFormat(ctx.out_pixfmt, \
        ctx.out_width, \
        ctx.out_height, \
        V4L2_NV_BUFFER_LAYOUT_PITCH);

块线性存储器格式符合tegra硬件

关键点

这个例程还是使用一个context_t的结构体来保存所有的资源。主要包含:

  • NvVideoConverter, 执行视频的转换
  • **std::queue< NvBuffer > conv_output_plane_buf_queue:给第二个转换器保存一个output plane 队列
    里面都包含哪些成员还是去查一下api的说明文档

这个例子写的有些复杂了。从上面的图可以看出,其实是两条实现途径的。一条是第一个NvVideoConverter直接output_plane直接到文件里,另一种方式上面说明的,使用了block-linear内存格式,所以需要第二个转换器再转换回pitch linear内存格式。

流程代码

/*
 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *  * Neither the name of NVIDIA CORPORATION nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "NvUtils.h"
#include <errno.h>
#include <fstream>
#include <iostream>
#include <malloc.h>
#include <string.h>
#include <unistd.h>

#include "video_convert.h"

#define TEST_ERROR(cond, str, label) if(cond) { \
                                        cerr << str << endl; \
                                        error = 1; \
                                        goto label; }

using namespace std;

static void
abort(context_t * ctx)
{
    ctx->got_error = true;
    ctx->conv0->abort();
    if (ctx->conv1)
    {
        ctx->conv1->abort();
    }
    pthread_cond_broadcast(&ctx->queue_cond);
}
//output_plane.dqBuffer调用时会执行这个函数
static bool
conv0_capture_dqbuf_thread_callback(struct v4l2_buffer *v4l2_buf,
                                   NvBuffer * buffer, NvBuffer * shared_buffer,
                                   void *arg)
{
    context_t *ctx = (context_t *) arg;
    NvBuffer *conv1_buffer;
    struct v4l2_buffer conv1_qbuf;
    struct v4l2_plane planes[MAX_PLANES];

    if (!v4l2_buf)
    {
        cerr << "Failed to dequeue buffer from conv0 capture plane" << endl;
        abort(ctx);
        return false;
    }

    if (ctx->in_buftype ==  BUF_TYPE_NVBL || ctx->out_buftype == BUF_TYPE_NVBL)
    {
        // Get an empty conv1 output plane buffer from conv1_output_plane_buf_queue
        pthread_mutex_lock(&ctx->queue_lock);
        while (ctx->conv1_output_plane_buf_queue->empty() && !ctx->got_error)
        {
            pthread_cond_wait(&ctx->queue_cond, &ctx->queue_lock);
        }

        if (ctx->got_error)
        {
            pthread_mutex_unlock(&ctx->queue_lock);
            return false;
        }
        conv1_buffer = ctx->conv1_output_plane_buf_queue->front();
        ctx->conv1_output_plane_buf_queue->pop();
        pthread_mutex_unlock(&ctx->queue_lock);

        memset(&conv1_qbuf, 0, sizeof(conv1_qbuf));
        memset(&planes, 0, sizeof(planes));

        conv1_qbuf.index = conv1_buffer->index;
        conv1_qbuf.m.planes = planes;

        // A reference to buffer is saved which can be used when
        // buffer is dequeued from conv1 output plane
        if (ctx->conv1->output_plane.qBuffer(conv1_qbuf, buffer)  < 0)
        {
            cerr << "Error queueing buffer on conv1 output plane" << endl;
            abort(ctx);
            return false;
        }

        if (v4l2_buf->m.planes[0].bytesused == 0)
        {
            return false;
        }
    }
    else
    {
        if (v4l2_buf->m.planes[0].bytesused == 0)
        {
            return false;
        }
        write_video_frame(ctx->out_file, *buffer);
        if (ctx->conv0->capture_plane.qBuffer(*v4l2_buf, buffer) < 0)
        {
            cerr << "Error queueing buffer on conv0 capture plane" << endl;
            abort(ctx);
            return false;
        }
    }
    return true;
}

static bool
conv1_output_dqbuf_thread_callback(struct v4l2_buffer *v4l2_buf,
                                  NvBuffer * buffer, NvBuffer * shared_buffer,
                                  void *arg)
{
    context_t *ctx = (context_t *) arg;
    struct v4l2_buffer conv0_ret_qbuf;
    struct v4l2_plane planes[MAX_PLANES];

    if (!v4l2_buf)
    {
        cerr << "Failed to dequeue buffer from conv1 output plane" << endl;
        abort(ctx);
        return false;
    }

    memset(&conv0_ret_qbuf, 0, sizeof(conv0_ret_qbuf));
    memset(&planes, 0, sizeof(planes));

    // Get the index of the conv0 capture plane shared buffer
    conv0_ret_qbuf.index = shared_buffer->index;
    conv0_ret_qbuf.m.planes = planes;

    // Add the dequeued buffer to conv1 empty output buffers queue to
    // conv1_output_plane_buf_queue
    // queue the shared buffer back in conv0 capture plane
    pthread_mutex_lock(&ctx->queue_lock);
    if (ctx->conv0->capture_plane.qBuffer(conv0_ret_qbuf, NULL) < 0)
    {
        cerr << "Error queueing buffer on conv0 capture plane" << endl;
        abort(ctx);
        return false;
    }
    ctx->conv1_output_plane_buf_queue->push(buffer);
    pthread_cond_broadcast(&ctx->queue_cond);
    pthread_mutex_unlock(&ctx->queue_lock);

    if (v4l2_buf->m.planes[0].bytesused == 0)
    {
        return false;
    }
    return true;
}

static bool
conv1_capture_dqbuf_thread_callback(struct v4l2_buffer *v4l2_buf,
                                   NvBuffer * buffer, NvBuffer * shared_buffer,
                                   void *arg)
{
    context_t *ctx = (context_t *) arg;

    if (!v4l2_buf)
    {
        cerr << "Failed to dequeue buffer from conv1 capture plane" << endl;
        abort(ctx);
        return false;
    }

    if (v4l2_buf->m.planes[0].bytesused == 0)
    {
        return false;
    }
    write_video_frame(ctx->out_file, *buffer);
    if (ctx->conv1->capture_plane.qBuffer(*v4l2_buf, NULL) < 0)
    {
        cerr << "Error queueing buffer on conv1 capture plane" << endl;
        abort(ctx);
        return false;
    }
    return true;
}

static void
set_defaults(context_t * ctx)
{
    memset(ctx, 0, sizeof(context_t));
    ctx->conv1_output_plane_buf_queue = new queue < NvBuffer * >;
    pthread_mutex_init(&ctx->queue_lock, NULL);
    pthread_cond_init(&ctx->queue_cond, NULL);

    ctx->interpolation_method = (enum v4l2_interpolation_method) -1;
    ctx->tnr_algorithm = (enum v4l2_tnr_algorithm) -1;
    ctx->flip_method = (enum v4l2_flip_method) -1;
}

int
main(int argc, char *argv[])
{
    context_t ctx;
    NvVideoConverter *main_conv;
    int ret = 0;
    int error = 0;
    bool eos = false;

    set_defaults(&ctx);

    ret = parse_csv_args(&ctx, argc, argv);
    TEST_ERROR(ret < 0, "Error parsing commandline arguments", cleanup);
    //不支持 BL to BL的转换
    TEST_ERROR(ctx.in_buftype == BUF_TYPE_NVBL &&
            ctx.out_buftype == BUF_TYPE_NVBL,
            "NV BL -> NV BL conversions are not supported in this sample", cleanup);

    ctx.in_file = new ifstream(ctx.in_file_path);
    TEST_ERROR(!ctx.in_file->is_open(), "Could not open input file", cleanup);

    ctx.out_file = new ofstream(ctx.out_file_path);
    TEST_ERROR(!ctx.out_file->is_open(), "Could not open output file", cleanup);

    ctx.conv0 = NvVideoConverter::createVideoConverter("conv0");
    TEST_ERROR(!ctx.conv0, "Could not create Video Converter", cleanup);
     //NVBL block linear PL pitch linear memory
    if (ctx.in_buftype == BUF_TYPE_NVBL || ctx.out_buftype == BUF_TYPE_NVBL)//不支持BL到BL,那么只要有一个是BL另一个就是PL的了,所以需要再创建一个converter
    {
        ctx.conv1 = NvVideoConverter::createVideoConverter("conv1");
        TEST_ERROR(!ctx.conv1, "Could not create Video Converter", cleanup);
    }
    //如果输入是BL,就使用conv1进行转换,如果输入是PL的就使用conv0进行转换
    if (ctx.in_buftype == BUF_TYPE_NVBL)
    {
        main_conv = ctx.conv1;
    }
    else
    {
        main_conv = ctx.conv0;
    }

    if (ctx.flip_method != -1)
    {
        ret = main_conv->setFlipMethod(ctx.flip_method);
        TEST_ERROR(ret < 0, "Could not set flip method", cleanup);
    }

    if (ctx.interpolation_method != -1)
    {
        ret = main_conv->setInterpolationMethod(ctx.interpolation_method);
        TEST_ERROR(ret < 0, "Could not set interpolation method", cleanup);
    }

    if (ctx.tnr_algorithm != -1)
    {
        ret = main_conv->setTnrAlgorithm(ctx.tnr_algorithm);
        TEST_ERROR(ret < 0, "Could not set tnr algorithm", cleanup);
    }

    if (ctx.crop_rect.width != 0 && ctx.crop_rect.height != 0)
    {
        ret = main_conv->setCropRect(ctx.crop_rect.left, ctx.crop_rect.top,
                ctx.crop_rect.width, ctx.crop_rect.height);
        TEST_ERROR(ret < 0, "Could not set crop rect for conv0",
                cleanup);
    }

    // Set conv0 output plane format
    ret = ctx.conv0->setOutputPlaneFormat(ctx.in_pixfmt, ctx.in_width,
                ctx.in_height, V4L2_NV_BUFFER_LAYOUT_PITCH);
    TEST_ERROR(ret < 0, "Could not set output plane format for conv0", cleanup);
    //如果输入是BL的
    if (ctx.in_buftype == BUF_TYPE_NVBL) //BL to PL
    {
    // Set conv0 capture plane format
    ret = ctx.conv0->setCapturePlaneFormat(ctx.in_pixfmt, ctx.in_width,
                ctx.in_height, V4L2_NV_BUFFER_LAYOUT_BLOCKLINEAR);
    }
    else if (ctx.out_buftype == BUF_TYPE_NVBL) //PL to BL
    {
    ret = ctx.conv0->setCapturePlaneFormat(ctx.out_pixfmt, ctx.out_width,
                ctx.out_height, V4L2_NV_BUFFER_LAYOUT_BLOCKLINEAR);
    }
    else //PL to PL
    {
    ret = ctx.conv0->setCapturePlaneFormat(ctx.out_pixfmt, ctx.out_width,
                ctx.out_height, V4L2_NV_BUFFER_LAYOUT_PITCH);
    }
    TEST_ERROR(ret < 0, "Could not set capture plane format for conv0", cleanup);

    if (ctx.in_buftype == BUF_TYPE_NVBL) //BL to PL
    {
    // Set conv1 output plane format
    ret =
        ctx.conv1->setOutputPlaneFormat(ctx.in_pixfmt, ctx.in_width,
                                       ctx.in_height, V4L2_NV_BUFFER_LAYOUT_BLOCKLINEAR);
    }
    else if (ctx.out_buftype == BUF_TYPE_NVBL) //PL to BL
    {
    ret =
        ctx.conv1->setOutputPlaneFormat(ctx.out_pixfmt, ctx.out_width,
                                       ctx.out_height, V4L2_NV_BUFFER_LAYOUT_BLOCKLINEAR);
    }
    TEST_ERROR(ret < 0, "Could not set output plane format for conv1", cleanup);

    if (ctx.conv1)
    {
        // Set conv1 capture plane format
        ret =
            ctx.conv1->setCapturePlaneFormat(ctx.out_pixfmt, ctx.out_width,
                    ctx.out_height, V4L2_NV_BUFFER_LAYOUT_PITCH);
        TEST_ERROR(ret < 0, "Could not set capture plane format for conv1",
                cleanup);
    }

    // REQBUF, EXPORT and MAP conv0 output plane buffers
    if (ctx.in_buftype == BUF_TYPE_RAW)
    {
    ret = ctx.conv0->output_plane.setupPlane(V4L2_MEMORY_USERPTR, 10, false, true);
    }
    else
    {
    ret = ctx.conv0->output_plane.setupPlane(V4L2_MEMORY_MMAP, 10, true, false);
    }
    TEST_ERROR(ret < 0, "Error while setting up output plane for conv0",
               cleanup);

    // REQBUF and EXPORT conv0 capture plane buffers
    // No need to MAP since buffer will be shared to next component
    // and not read in application
    if (ctx.out_buftype == BUF_TYPE_RAW && ctx.in_buftype != BUF_TYPE_NVBL)
    {
    ret =
        ctx.conv0->capture_plane.setupPlane(V4L2_MEMORY_USERPTR, 10, false, true);
    }
    else if (ctx.in_buftype == BUF_TYPE_NVBL || ctx.out_buftype == BUF_TYPE_NVBL)
    {
    ret = ctx.conv0->capture_plane.setupPlane(V4L2_MEMORY_MMAP, 10, false, false);
    }
    else
    {
    ret =
        ctx.conv0->capture_plane.setupPlane(V4L2_MEMORY_MMAP, 10, true, false);
    }
    TEST_ERROR(ret < 0, "Error while setting up capture plane for conv0",
               cleanup);

    // conv0 output plane STREAMON
    ret = ctx.conv0->output_plane.setStreamStatus(true);
    TEST_ERROR(ret < 0, "Error in output plane streamon for conv0", cleanup);

    // conv0 capture plane STREAMON
    ret = ctx.conv0->capture_plane.setStreamStatus(true);
    TEST_ERROR(ret < 0, "Error in capture plane streamon for conv0", cleanup);

    ctx.conv0->
        capture_plane.setDQThreadCallback(conv0_capture_dqbuf_thread_callback);

    if (ctx.conv1)
    {
        // REQBUF on conv1 output plane buffers
        // DMABUF is used here since it is a shared buffer allocated but another
        // component
        ret = ctx.conv1->output_plane.setupPlane(V4L2_MEMORY_DMABUF, 10, false,
                    false);
        TEST_ERROR(ret < 0, "Error while setting up output plane for conv1",
                cleanup);

        // REQBUF on conv1 capture plane buffers,
        // Since USERPTR is used, memory is allocated by the application
        if (ctx.out_buftype == BUF_TYPE_RAW)
        {
            ret = ctx.conv1->capture_plane.setupPlane(V4L2_MEMORY_USERPTR, 10,
                        false, true);
        }
        else
        {
            ret = ctx.conv1->capture_plane.setupPlane(V4L2_MEMORY_MMAP, 10,
                    true, false);
        }
        TEST_ERROR(ret < 0, "Error while setting up capture plane for conv1",
                cleanup);

        // conv1 output plane STREAMON
        ret = ctx.conv1->output_plane.setStreamStatus(true);
        TEST_ERROR(ret < 0, "Error in output plane streamon for conv1", cleanup);

        // conv1 capture plane STREAMON
        ret = ctx.conv1->capture_plane.setStreamStatus(true);
        TEST_ERROR(ret < 0, "Error in capture plane streamon for conv1", cleanup);

        ctx.conv1->output_plane.setDQThreadCallback(
                conv1_output_dqbuf_thread_callback);
        ctx.conv1->capture_plane.setDQThreadCallback(
                conv1_capture_dqbuf_thread_callback);
    }

    // Start threads to dequeue buffers on conv0 capture plane,
    // conv1 output plane and conv1 capture plane
    ctx.conv0->capture_plane.startDQThread(&ctx);
    if (ctx.conv1)
    {
        ctx.conv1->output_plane.startDQThread(&ctx);
        ctx.conv1->capture_plane.startDQThread(&ctx);
    }

    // Enqueue all empty conv0 capture plane buffers
    for (uint32_t i = 0; i < ctx.conv0->capture_plane.getNumBuffers(); i++)
    {
        struct v4l2_buffer v4l2_buf;
        struct v4l2_plane planes[MAX_PLANES];

        memset(&v4l2_buf, 0, sizeof(v4l2_buf));
        memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));

        v4l2_buf.index = i;
        v4l2_buf.m.planes = planes;

        ret = ctx.conv0->capture_plane.qBuffer(v4l2_buf, NULL);
        if (ret < 0)
        {
            cerr << "Error while queueing buffer at conv0 capture plane" << endl;
            abort(&ctx);
            goto cleanup;
        }
    }

    if (ctx.conv1)
    {
        // Add all empty conv1 output plane buffers to conv1_output_plane_buf_queue
        for (uint32_t i = 0; i < ctx.conv1->output_plane.getNumBuffers(); i++)
        {
            ctx.conv1_output_plane_buf_queue->push(ctx.conv1->output_plane.
                    getNthBuffer(i));
        }

        // Enqueue all empty conv1 capture plane buffers
        for (uint32_t i = 0; i < ctx.conv1->capture_plane.getNumBuffers(); i++)
        {
            struct v4l2_buffer v4l2_buf;
            struct v4l2_plane planes[MAX_PLANES];

            memset(&v4l2_buf, 0, sizeof(v4l2_buf));
            memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));

            v4l2_buf.index = i;
            v4l2_buf.m.planes = planes;

            ret = ctx.conv1->capture_plane.qBuffer(v4l2_buf, NULL);
            if (ret < 0)
            {
                cerr << "Error while queueing buffer at conv1 capture plane"
                    << endl;
                abort(&ctx);
                goto cleanup;
            }
        }
    }

    // Read video frame from file and queue buffer on conv0 output plane
    for (uint32_t i = 0; i < ctx.conv0->output_plane.getNumBuffers() &&
            !ctx.got_error && !eos; i++)
    {
        struct v4l2_buffer v4l2_buf;
        struct v4l2_plane planes[MAX_PLANES];
        NvBuffer *buffer = ctx.conv0->output_plane.getNthBuffer(i);

        memset(&v4l2_buf, 0, sizeof(v4l2_buf));
        memset(planes, 0, MAX_PLANES * sizeof(struct v4l2_plane));

        v4l2_buf.index = i;
        v4l2_buf.m.planes = planes;
         //直接读到了NvBuffer中,这个是应用程序空间的指针,在前面指定了V4L2_MEMORY_USERPTR (ctx.conv0->output_plane.setupPlane(V4L2_MEMORY_USERPTR, 10, false, true));
        if (read_video_frame(ctx.in_file, *buffer) < 0)
        {
            cerr << "Could not read complete frame from input file" << endl;
            cerr << "File read complete." << endl;
            v4l2_buf.m.planes[0].bytesused = 0;
            eos = true;
        }

        ret = ctx.conv0->output_plane.qBuffer(v4l2_buf, NULL);
        if (ret < 0)
        {
            cerr << "Error while queueing buffer at conv0 output plane" << endl;
            abort(&ctx);
            goto cleanup;
        }
    }

    // Read video frame from file till EOS is reached and queue buffer on conv0 output plane
    while (!ctx.got_error && !ctx.conv0->isInError() &&
            (!ctx.conv1 || !ctx.conv1->isInError()) && !eos)
    {
        struct v4l2_buffer v4l2_buf;
        struct v4l2_plane planes[MAX_PLANES];
        NvBuffer *buffer;

        memset(&v4l2_buf, 0, sizeof(v4l2_buf));
        memset(planes, 0, sizeof(planes));

        v4l2_buf.m.planes = planes;
        //触发一下dequeue 然后再queue一下
        if (ctx.conv0->output_plane.dqBuffer(v4l2_buf, &buffer, NULL, 100) < 0)
        {
            cerr << "ERROR while DQing buffer at conv0 output plane" << endl;
            abort(&ctx);
            goto cleanup;
        }

        if (read_video_frame(ctx.in_file, *buffer) < 0)
        {
            cerr << "Could not read complete frame from input file" << endl;
            cerr << "File read complete." << endl;
            v4l2_buf.m.planes[0].bytesused = 0;
            eos = true;
        }

        ret = ctx.conv0->output_plane.qBuffer(v4l2_buf, NULL);
        if (ret < 0)
        {
            cerr << "Error while queueing buffer at conv0 output plane" << endl;
            abort(&ctx);
            goto cleanup;
        }
    }

    if (!ctx.got_error)
    {
        // Wait till all capture plane buffers on conv0 and conv1 are dequeued
        ctx.conv0->waitForIdle(-1);
        if (ctx.conv1)
        {
            ctx.conv1->waitForIdle(-1);
        }
    }

cleanup:
    if (ctx.conv0 && ctx.conv0->isInError())
    {
        cerr << "VideoConverter0 is in error" << endl;
        error = 1;
    }

    if (ctx.conv1 && ctx.conv1->isInError())
    {
        cerr << "VideoConverter1 is in error" << endl;
        error = 1;
    }

    if (ctx.got_error)
    {
        error = 1;
    }

    delete ctx.conv1_output_plane_buf_queue;
    delete ctx.in_file;
    delete ctx.out_file;
    // Destructors do all the cleanup, unmapping and deallocating buffers
    // and calling v4l2_close on fd
    delete ctx.conv0;
    delete ctx.conv1;

    free(ctx.in_file_path);
    free(ctx.out_file_path);

    if (error)
    {
        cout << "App run failed" << endl;
    }
    else
    {
        cout << "App run was successful" << endl;
    }
    return -error;
}

对于这个例子的pitch linear 到pitch linear的流程看明白了,其它的方式有一些不明白的地方,比如什么时候用image_raw的映射方式。希望有高手指点交流一下。
微信:topeet

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值