v4l2解码调用流程

最近在学习v4l2codec,下面把学习心得总结一下,说明一点,这里不讲解v4l2,主要介绍应用层如何调用写案例,相关v4l2请看:
1、深入理解v4l2buf
2、V4L2学习资料收集
3、V4L2学习笔记

这个案例中,是V4L2_MEMORY_MMAP方式分配内存。

流程:

  • 设置输入端口的格式
  • 设置输出端口的格式
  • 枚举输入端口buf数量
  • map输入端口的buf(while循环)
  • 枚举输出端口的buf数量
  • map输出端口的buf(while循环)
  • 输出端口buf入队
  • stream on(输入和输出)
  • 输入数据入队
  • 循环从v4l2中取出已经解码后的数据,并且把空的buf送入v4l2(输出端口)
  • 循环从v4l2中取出已经消耗的buf,重新填充buf后送入v4l2

说明,下面代码是一次性把输入读取完,送入到了v4l2,实际情况下,我们并不是这样,而是循环送入,具体可以看这个案例:
入口
具体代码如下:

// #include <linux/videodev2.h>
// #include <cstdint>
#include "common.h"

//
// ======== comm =======
int mem_type = V4L2_MEMORY_MMAP;
#define INPUT_BUF_NUM (2)
#define OUTPUT_BUF_NUM (5)
#define BS_BUF_SIZE (1 << 20)

// ======== in =======
int                in_fourcc   = V4L2_PIX_FMT_H264;
enum v4l2_buf_type in_buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;

struct v4l2_format in_format;
struct v4l2_buffer in_buffer_arr[INPUT_BUF_NUM];
struct v4l2_plane  in_buf_planes[INPUT_BUF_NUM][VIDEO_MAX_PLANES];
char *             in_user_ptr[INPUT_BUF_NUM][8];

// ======== out =======
int                out_fourcc   = V4L2_PIX_FMT_YUV420M;
enum v4l2_buf_type out_buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;

struct v4l2_format out_format;
struct v4l2_buffer out_buffer_arr[OUTPUT_BUF_NUM];
struct v4l2_plane  out_buf_planes[OUTPUT_BUF_NUM][VIDEO_MAX_PLANES];
char *             out_user_ptr[OUTPUT_BUF_NUM][8];

// =============================================

int ioctl_handler(int fd, int req, void *data) {
    DBG_SHOW_FUNC;
    int ret = ioctl(fd, req, data);
    if (0 != ret) {
        LOG("=====> IOCTL ERROR, req = %08x\n", req);
    }

    return ret;
}

int ioc_set_ctrl(int fd, int id, int val) {
    DBG_SHOW_FUNC;
    int                 ret = 0;
    struct v4l2_control ctrl;

    memset(&ctrl, 0, sizeof(ctrl));
    ctrl.id    = id;
    ctrl.value = val;

    ret = ioctl_handler(fd, VIDIOC_S_CTRL, &ctrl);
    return ret;
}

int get_format(int fd, struct v4l2_format *fmt, enum v4l2_buf_type buf_type) {
    DBG_SHOW_FUNC;
    fmt->type = buf_type;
    return ioctl_handler(fd, VIDIOC_G_FMT, fmt);
}

int set_format(int fd, struct v4l2_format *fmt) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_S_FMT, fmt);
}

int try_format(int fd, struct v4l2_format *fmt) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_TRY_FMT, fmt);
}

int req_buffers(int fd, struct v4l2_requestbuffers *reqbuf) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_REQBUFS, reqbuf);
}

int query_buffer(int fd, struct v4l2_buffer *buf) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_QUERYBUF, buf);
}

int queue_buffer(int fd, struct v4l2_buffer *buf) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_QBUF, buf);
}

int dequeue_buffer(int fd, struct v4l2_buffer *buf) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_DQBUF, buf);
}

int stream_on(int fd, enum v4l2_buf_type *type) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_STREAMON, type);
}

int stream_off(int fd, enum v4l2_buf_type *type) {
    DBG_SHOW_FUNC;
    return ioctl_handler(fd, VIDIOC_STREAMOFF, type);
}

int get_output_picture(int fd, int pic_idx) {
    DBG_SHOW_FUNC;
    int ret = 0;

    struct v4l2_buffer buf;
    struct v4l2_plane  planes[8];
    buf.m.planes = planes;
    buf.type     = out_buf_type;
    buf.memory   = mem_type;
    buf.length   = 3;

    ret = dequeue_buffer(fd, &buf);
    if (0 != ret) {
        LOG("ERROR: no more output picture\n");
        return -1;
    }

    out_buffer_arr[buf.index] = buf;
    if (V4L2_TYPE_IS_MULTIPLANAR(buf.type)) {
        out_buffer_arr[buf.index].m.planes = out_buf_planes[buf.index];
        for (size_t i = 0; i < out_buffer_arr[buf.index].length; ++i) {
            out_buffer_arr[buf.index].m.planes[i] = buf.m.planes[i];
        }
    }

    return buf.index;
}

int map_memory(int fd, struct v4l2_buffer *buf, void *user_ptr[8]) {
    if (V4L2_TYPE_IS_MULTIPLANAR(buf->type)) {
        for (int i = 0; i < buf->length; ++i) {
            struct v4l2_plane *p = &buf->m.planes[i];
            if (p->length > 0) {
                user_ptr[i] = mmap(
                    NULL, p->length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, p->m.mem_offset);
                if (user_ptr[i] == MAP_FAILED) {
                    LOG("====== Failed to mmap multi plane memory");
                    return -1;
                }
            }
        }
    } else {
        if (buf->length > 0) {
            user_ptr[0] =
                mmap(NULL, buf->length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf->m.offset);
            if (user_ptr[0] == MAP_FAILED) {
                LOG("============= Failed to mmap single plane memory");
                return -2;
            }
        }
    }
}

void show_buffer_info(struct v4l2_buffer *p) {
    LOG("======= v4l2_buffer =====\n");
    LOG("index:             %d\n", p->index);
    LOG("type:              %d\n", p->type);
    LOG("bytesused:         %d\n", p->bytesused);
    LOG("flags:             0x%08x\n", p->flags);
    LOG("timeval sec:       %ld\n", p->timestamp.tv_sec);
    LOG("timeval usec:      %ld\n", p->timestamp.tv_usec);
    LOG("timecode type:     %d\n", p->timecode.type);
    LOG("timecode flags:    0x%08x\n", p->timecode.flags);
    LOG("timecode frames:   %d\n", p->timecode.frames);
    LOG("timecode seconds:  %d\n", p->timecode.seconds);
    LOG("timecode minutes:  %d\n", p->timecode.minutes);
    LOG("timecode hours:    %d\n", p->timecode.hours);
    LOG("timecode userbits: %d\n", (uint32_t)p->timecode.userbits);
    LOG("sequence:          %d\n", p->sequence);
    LOG("length:            %d\n", p->length);
    LOG("reserved2:         %08x\n", p->reserved2);
}

int init_decoder(int fd) {
    int ret = 0;
    DBG_SHOW_FUNC;
    //
    // set naul format
    ioc_set_ctrl(fd, V4L2_CID_MVE_VIDEO_NALU_FORMAT, V4L2_OPT_NALU_FORMAT_START_CODES);
    // set format
    {
        LOG("=========== H264 format ============\n");
        int w = 0, h = 0;
        ret = get_format(fd, &in_format, in_buf_type);

        struct v4l2_pix_format *f = &in_format.fmt.pix;

        f->pixelformat  = in_fourcc;
        f->width        = w;
        f->height       = h;
        f->bytesperline = 0;
        f->sizeimage    = (1 << 20);
        f->field        = V4L2_FIELD_NONE;

        ret = try_format(fd, &in_format);
        w   = f->width;
        h   = f->height;
        LOG("After try_format, w = %d, h = %d\n", w, h);
        ret = set_format(fd, &in_format);
    }
    {
        LOG("============YUV420 format ==========\n");
        int w = 0, h = 0;
        ret = get_format(fd, &out_format, out_buf_type);

        struct v4l2_pix_format_mplane *f = &out_format.fmt.pix_mp;

        f->pixelformat = out_fourcc;
        f->width       = w;
        f->height      = h;
        f->num_planes  = 3;
        f->field       = V4L2_FIELD_NONE;

        ret = try_format(fd, &out_format);
        w   = f->width;
        h   = f->height;
        LOG("After try_format, w = %d, h = %d\n", w, h);
        ret = set_format(fd, &out_format);
    }

    // enum frame sizes

    {
        // alloc buffers
        struct v4l2_requestbuffers reqbuf;
        reqbuf.count  = 1;
        reqbuf.type   = in_buf_type;
        reqbuf.memory = mem_type;

        ret = req_buffers(fd, &reqbuf);
        LOG("=======> bitstream buffer count : %d\n", reqbuf.count);
        for (int i = 0; i < reqbuf.count; ++i) {
            in_buffer_arr[i].type     = in_buf_type;
            in_buffer_arr[i].memory   = mem_type;
            in_buffer_arr[i].m.planes = in_buf_planes[i];
            in_buffer_arr[i].index    = i;
            in_buffer_arr[i].length   = 3;

            ret = query_buffer(fd, &in_buffer_arr[i]);

            if (V4L2_MEMORY_MMAP == mem_type) {
                ret = map_memory(fd, &in_buffer_arr[i], in_user_ptr[i]);
            }
        }

        struct v4l2_requestbuffers reqbuf_out;
        reqbuf_out.count  = OUTPUT_BUF_NUM;
        reqbuf_out.type   = out_buf_type;
        reqbuf_out.memory = mem_type;

        ret = req_buffers(fd, &reqbuf_out);
        LOG("=======> output yuv buffer count : %d\n", reqbuf.count);
        for (int i = 0; i < reqbuf_out.count; ++i) {
            out_buffer_arr[i].type     = out_buf_type;
            out_buffer_arr[i].memory   = mem_type;
            out_buffer_arr[i].m.planes = out_buf_planes[i];
            out_buffer_arr[i].index    = i;
            out_buffer_arr[i].length   = 3;

            ret = query_buffer(fd, &out_buffer_arr[i]);

            if (V4L2_MEMORY_MMAP == mem_type) {
                ret = map_memory(fd, &out_buffer_arr[i], out_user_ptr[i]);
            }
        }
    }

    // queue buffer
    for (int i = 0; i < OUTPUT_BUF_NUM; ++i) {
        // remove vendor custom flags.
        out_buffer_arr[i].flags &= ~V4L2_BUF_FLAG_MVX_MASK;

        // mask buffer offset
        if (!V4L2_TYPE_IS_MULTIPLANAR(out_buffer_arr[i].type) &&
            V4L2_MEMORY_MMAP == out_buffer_arr[i].memory) {
            out_buffer_arr[i].m.offset &= ~((1 << 12) - 1);
        }

        ret = queue_buffer(fd, &out_buffer_arr[i]);
    }

    // stream on
    stream_on(fd, &in_buf_type);
    stream_on(fd, &out_buf_type);

    return ret;
}

int decode_file(int fd, const char *bs_filename) {
    DBG_SHOW_FUNC;
    int ret = 0;

    // init decoder
    ret = init_decoder(fd);
    // input bs data
    {

        FILE *pbs_file = fopen(bs_filename, "wb");
        if (!pbs_file) {
            LOG("ERROR: can not open bs file: %s\n", bs_filename);
        }
        // read data
        fseek(pbs_file, 0, SEEK_END);
        int bs_len = ftell(pbs_file);
        fseek(pbs_file, 0, SEEK_SET);

        // char *bs_data  = malloc(bs_len * sizeof(char));
        //
        in_buffer_arr[0].flags = 0;
        LOG("bs data len: %d, bs_buf size: %d", bs_len, in_buffer_arr[0].length);
        int read_len = fread(in_user_ptr[0], sizeof(char), bs_len, pbs_file);
        LOG("read bs len : %d, bs buf size: %d\n", read_len, in_buffer_arr[0].length);

        //
        in_buffer_arr[0].bytesused = read_len;
        in_buffer_arr[0].flags |= V4L2_BUF_FLAG_LAST;

        if (pbs_file) {
            fclose(pbs_file);
        }

        // queue
        ret = queue_buffer(fd, &in_buffer_arr[0]);
    }

    // get output picture
    {
        //
        int   buf_idx = 0;  // out_buffer_arr index
        int   pic_idx = 0;
        FILE *out_pic = fopen("out.yuv", "wb");

        do {
            buf_idx = get_output_picture(fd, pic_idx);

            // handle output picture
            struct v4l2_buffer *p        = &out_buffer_arr[buf_idx];
            char *              user_ptr = out_user_ptr[buf_idx];
            show_buffer_info(p);
            for (int i = 0; i < p->length; ++i) {
                struct v4l2_plane *plane = &p->m.planes;

                char *pl     = user_ptr[i] + plane->data_offset;
                int   len    = plane->bytesused - plane->data_offset;
                int   wr_len = fwrite(pl, 1, len, out_pic);
            }

            pic_idx++;
            // check last output picture

        } while (-1 != buf_idx);

        if (out_pic) {
            fclose(out_pic);
        }
    }

    // stream off
    stream_off(fd, &in_buf_type);
    stream_off(fd, &out_buf_type);

    return ret;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值