最近在学习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;
}