V4L2_Record.c 9.2 KB


/* Includes ------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h> /* getopt_long() */
#include <fcntl.h>  /* low-level i/o */
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include <linux/videodev2.h>

#include "../CameraParameter.h"
#include "version_choose.h"
#include "UI_control.h"
#include "BaseConfig.h"

#include "./V4L2_Record.h"

#include "JZsdkLib.h"

/* Private constants ---------------------------------------------------------*/
// 清空
#define CLEAR(x) memset(&(x), 0, sizeof(x))

/* Private types -------------------------------------------------------------*/
unsigned char *returnptr;//保存映射后用户空间的首地址
unsigned int returnsize;

struct buffer
{
        char *start;
        size_t length;
};

struct v4l2_buffer H3_ReturnBuffer;

// 设备号
static int fd = -1; // DEVICE NUMBER

// 存储队列
static struct buffer *buffers;
static unsigned int n_buffers;
static struct v4l2_buffer buf;
//unsigned char* pucVideBuf[4];  // 视频BUFF空间地址

/* Private functions declaration ---------------------------------------------*/
static int xioctl(int fh, int request, void *arg)
{
        int r;
        do
        {
            r = ioctl(fh, request, arg);
        } while (-1 == r );
        return r;
}

static T_JZsdkReturnCode stop_capturing()
{
    enum v4l2_buf_type type;

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
    {
        JZSDK_LOG_ERROR("VddIDIOC_STREAMOFF");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

static T_JZsdkReturnCode start_capturing()
{
    unsigned int i;
    enum v4l2_buf_type type;

    for (i = 0; i < n_buffers; ++i)
    {
        //struct v4l2_buffer buf;

        CLEAR(buf);
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP; //内存映射
        buf.index = i;

        //从缓存区中取出数据
        if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
        {
            JZSDK_LOG_ERROR("VIDIOC_QBUF");
        }
    }

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
    {
        JZSDK_LOG_ERROR("VIDIOC_STREAMON");
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

static T_JZsdkReturnCode uninit_device()
{
    unsigned int i;

    for (i = 0; i < n_buffers; ++i)
    {
        if (-1 == munmap(buffers[i].start, buffers[i].length))
        {
            JZSDK_LOG_ERROR("munmap");
        }
    }
    free(buffers);
}

static T_JZsdkReturnCode init_mmap()
{
    //定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。
    struct v4l2_requestbuffers req;

    CLEAR(req);

    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1)
    {
        JZSDK_LOG_ERROR("VIDIOC_REQBUFS");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    if (req.count < 2)
    {
        JZSDK_LOG_ERROR("摄像头mmap的缓冲区内存不足");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    //分配req.count个 *Buffer长度的内存
    buffers = calloc(req.count, sizeof(*buffers));

    if (!buffers)
    {
        JZSDK_LOG_ERROR("摄像头buffers注册内存失败");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    // 改变buffer数量 以改进视频质量
    //映射所有的缓存
    for (n_buffers = 0; n_buffers < req.count; ++n_buffers)
    {
        CLEAR(buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = n_buffers;

        if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
        {
            JZSDK_LOG_ERROR("VIDIOC_QUERYBUF");
        }

        buffers[n_buffers].length = buf.length;
        buffers[n_buffers].start =
            mmap(NULL /* start anywhere */,
                    buf.length,
                    PROT_READ | PROT_WRITE /* required */,
                    MAP_SHARED /* recommended */,
                    fd, buf.m.offset);

        if (MAP_FAILED == buffers[n_buffers].start)
        {
            JZSDK_LOG_ERROR("mmap");
        }
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}
// 初始化 程序
static T_JZsdkReturnCode init_device()
{
    struct v4l2_capability cap;
    struct v4l2_format fmt;

    if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap))
    { //测试参数
        JZSDK_LOG_ERROR("VIDIOC_QUERYCAP");
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
    {
        JZSDK_LOG_ERROR("no video capture device");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    if (!(cap.capabilities & V4L2_CAP_STREAMING))
    {
        JZSDK_LOG_ERROR("does not support streaming i/o");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    // 设置摄像头使用格式
    memset(&fmt, 0, sizeof(struct v4l2_format));

    //描绘帧的格式
    // fmt结构体 含有 tpye格式 和 fmt 联合体
    // fmt 结构体
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = CAMERA_RECORD_WIDTH;
    fmt.fmt.pix.height = CAMERA_RECORD_HEIGHT;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    //写入格式参数
    if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) //设置格式
    {
        JZSDK_LOG_ERROR("VIDIOC_S_FMT");
    }

    /*
            修改: 加入帧数设置
    */
    struct v4l2_streamparm streamparm;
    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    streamparm.parm.capture.timeperframe.numerator = 1;    //每1秒
    streamparm.parm.capture.timeperframe.denominator = CAMERA_RECORD_FRAME; // 帧数

    streamparm.parm.capture.capturemode  = 0; //低质量图片模式

    //写入帧数设置
    if (-1 == xioctl(fd, VIDIOC_S_PARM, &streamparm))
    {
        JZSDK_LOG_ERROR("failed in setting the fps in camera!");
    }
    printf("camera is getting img-data in %d fps\n", streamparm.parm.capture.timeperframe.denominator);

    //初始化映射
    T_JZsdkReturnCode ret = init_mmap();
    if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS)
    {
        return ret;
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

static T_JZsdkReturnCode close_device()
{
    if (-1 == close(fd))
    {
        JZSDK_LOG_ERROR("close");
    }

    fd = -1;
}

static T_JZsdkReturnCode open_device()
{
    //加上O_NONBLOCK会出现如下错误
    // VIDIOC_DQBUF error 11, Resource temporarily unavailable
    //fd = open(CAMERA_DEVICE_NAME, O_RDWR | O_NONBLOCK, 0);
    fd = open(CAMERA_DEVICE_NAME, O_RDWR , 0);

    if (-1 == fd)
    {
        JZSDK_LOG_ERROR("打开摄像头设备错误");
        return JZ_ERROR_SYSTEM_MODULE_CODE_FAILURE;
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

T_JZsdkReturnCode V4l2_Camarainit(int *dev_fd)
{
    T_JZsdkReturnCode ret;

    //1、打开摄像头
    ret = open_device();
    if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS)
    {
        JZSDK_LOG_ERROR("打开摄像头失败");
        return ret;
    }
    
    //初始化程序
    ret = init_device();
    if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS)
    {
        JZSDK_LOG_ERROR("初始化摄像头设备失败");
        return ret;
    }

    //开始抓取
    ret = start_capturing();
    if (ret != JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS)
    {
        JZSDK_LOG_ERROR("开始抓取失败");
        return ret;
    }

    *dev_fd = fd;

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

T_JZsdkReturnCode V4L2_CamaraUninit()
{
          
    //停止采集
	stop_capturing();

    //释放设备
    uninit_device();

    //关闭设备
    close_device();

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}

/***********
 * 
 *  取出画面数据
 * 
 * ************/
T_JZsdkReturnCode V4L2_CameraFrameRecord(char **RecordReturnBuffer, int *RecordReturnLength, int Audlength)
{
    memset(&buf, 0, sizeof(struct v4l2_buffer));

    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //每个结构体都需要设置type为这个参数要记住
    buf.memory = V4L2_MEMORY_MMAP;

    //取出画面
    if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf))
    {
        JZSDK_LOG_ERROR("VIDIOC_DQBUF");
    }

    //画面长度
    *RecordReturnLength = buf.bytesused;
    
    //画面数组
    *RecordReturnBuffer = (unsigned char*)malloc(*RecordReturnLength + Audlength);
    if(RecordReturnBuffer == NULL)
    {
        JZSDK_LOG_ERROR("摄像头的视频回复帧内存注册错误");
    }

    memcpy(*RecordReturnBuffer, buffers[buf.index].start, *RecordReturnLength);

    //放回画面内存
    if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
    {
        JZSDK_LOG_ERROR("VIDIOC_QBUF");
    }

    return JZ_ERROR_SYSTEM_MODULE_CODE_SUCCESS;
}