#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <malloc.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <pthread.h>
#include <linux/videodev2.h>
#include <libv4l2.h>
#include "sowebcam-linux.h"
#define VIDIOC_G_FRAMERATE _IOWR('V', 200, int)
#define VIDIOC_S_FRAMERATE _IOWR('V', 201, int)
#define VIDIOC_G_COMPQUALITY _IOWR('V', 202, int)
#define VIDIOC_S_COMPQUALITY _IOWR('V', 203, int)
#define INIT_ID 0xFE567DA0
typedef struct tagSoV4l2Buffer
{
void* start;
size_t length;
}SoV4l2Buffer;
static struct SoVCData
{
unsigned int init_id;
SOLCAM_STATUS status;
int stopped;
int h264_fd;
SoV4l2Buffer* h264_buffers;
unsigned int h264_buffer_num;
FN_CAPTURE_CALLBACK fn_capture;
FN_ERROR_CALLBACK fn_error;
double cur_framerate;
int cur_bitrate;
}sovc_data;
const char *sp_h264_dev = "/dev/v4l/by-id/usb-Ningbo_Sunny_Opotech_Co._Ltd_Sunny_HD_WebCam_UVC_UAC__1.0.1-video-index0";
static int is_file_exist(const char* file_name)
{
struct stat namestat;
if(file_name == NULL || strlen(file_name) == 0)
return 0;
if(stat(file_name, &namestat) == -1)
return 0;
return 1;
}
static int xioctl(int fd, int request,void *arg)
{
int r;
do r = ioctl (fd, request, arg);
while (-1 == r && EINTR == errno);
return r;
}
SOERR solcam_vc_init()
{
if(sovc_data.init_id == INIT_ID)
return SOERR_NONE;
if(!is_file_exist(sp_h264_dev))
{
printf("the sunny-webcam device is not been pluged\n");
return SOERR_NO_CAM_DEVICE;
}
memset(&sovc_data, 0, sizeof(sovc_data));
sovc_data.status = SOLCAM_STATUS_STOPPED;
sovc_data.init_id = INIT_ID;
return SOERR_NONE;
}
static long get_tick_count()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return (tv.tv_sec * 1000 + tv.tv_usec/1000);
}
static void test_fps_bitrate(int frame_size)
{
static int frames_num =0;
static long start_time = 1;
static long total_frame_size = 0;
if(start_time == 1)
{
start_time = get_tick_count();
}
frames_num++;
total_frame_size += frame_size;
if(frames_num%30 == 0)
{
sovc_data.cur_framerate = (double)frames_num*1000/(get_tick_count()-start_time);
sovc_data.cur_bitrate = (int)(total_frame_size*1000/(get_tick_count()-start_time));
start_time = get_tick_count();
frames_num = 0;
total_frame_size = 0;
}
}
static void* video_capture_thread(void* param)
{
struct v4l2_buffer buf;
enum v4l2_buf_type type;
int i = 0;
param = param;
sovc_data.status = SOLCAM_STATUS_RUNNING;
sovc_data.stopped = 0;
// printf("enter video capture thread\n");
while(sovc_data.status == SOLCAM_STATUS_RUNNING)
{
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
//printf("capture one frame-2\n");
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_DQBUF, &buf))
{
if(sovc_data.fn_error)
sovc_data.fn_error("failed dqbuf\n");
break;
}
//printf("capture one frame-1\n");
if(sovc_data.fn_capture)
{
sovc_data.fn_capture(sovc_data.h264_buffers[buf.index].start, buf.bytesused);
test_fps_bitrate(buf.bytesused);
// printf("capture one frame\n");
}
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_QBUF, &buf))
{
if(sovc_data.fn_error)
sovc_data.fn_error("failed qbuf\n");
break;
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//printf("exit video capture thread\n");
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_STREAMOFF, &type))
{
printf("VIDIOC_STREAMOFF failed\n");
}
sovc_data.status = SOLCAM_STATUS_STOPPED;
for (i = 0; i < sovc_data.h264_buffer_num; ++i)
{
munmap (sovc_data.h264_buffers[i].start, sovc_data.h264_buffers[i].length);
}
free(sovc_data.h264_buffers);
sovc_data.stopped = 1;
//printf("exit video capture thread-1\n");
return NULL;
}
#ifndef V4L2_PIX_FMT_H264
#define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4') /* H264 stream */
#endif
static SOERR vc_set_res(VideoRes* video_res)
{
struct v4l2_format fmt;
if(video_res == NULL)
{
return SOERR_INVALID_PARAM;
}
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.status == SOLCAM_STATUS_RUNNING)
{
return SOERR_WRONG_STATUS;
}
memset(&fmt, 0, sizeof(struct v4l2_format));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = video_res->width;
fmt.fmt.pix.height = video_res->height;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_H264;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_S_FMT, &fmt))
{
printf("failed set yo resolution\n");
return SOERR_FAILED_SET_RES;
}
return SOERR_NONE;
}
static SOERR vc_set_framerate(int framerate)
{
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if (v4l2_ioctl (sovc_data.h264_fd, VIDIOC_S_FRAMERATE, &framerate) < 0)
{
printf("get brightness failed!\n");
return SOERR_FAILED_SET_FRAMERATE;
}
return SOERR_NONE;
}
SOERR solcam_vc_run(VideoRes *res, int fps, FN_CAPTURE_CALLBACK fn_capture, FN_ERROR_CALLBACK fn_error)
{
enum v4l2_buf_type type;
struct v4l2_capability cap;
struct v4l2_requestbuffers req;
int i = 0;
SOERR ret = SOERR_FAILED_START_VIDEO_CAP;
if(!is_file_exist(sp_h264_dev))
{
printf("the sunny-webcam device is not been pluged\n");
return SOERR_NO_CAM_DEVICE;
}
if(res == NULL)
return SOERR_INVALID_PARAM;
if(fps!=10 && fps!=15 && fps!=20 && fps!=25 && fps!=30)
return SOERR_INVALID_PARAM;
sovc_data.h264_fd = open(sp_h264_dev, O_RDWR);
if(sovc_data.h264_fd <= 0)
{
printf("sunny webcam device can not be opened!\n");
goto error;
}
ret = vc_set_res(res);
if(ret != SOERR_NONE)
{
goto error;
}
ret = vc_set_framerate(fps);
if(ret != SOERR_NONE)
{
goto error;
}
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_QUERYCAP, &cap))
{
printf("ioctl VIDIOC_QUERYCAP failed\n");
goto error;
}
if(!((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) && (cap.capabilities & V4L2_CAP_STREAMING)))
{
printf ("no video capture device\n");
goto error;
}
memset(&req, 0, sizeof(struct v4l2_requestbuffers));
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_REQBUFS, &req))
{
if(EINVAL == errno)
{
printf("does not support memory mapping\n");
}
printf("VIDIOC_REQBUFS\n");
goto error;
}
if (req.count < 2)
{
printf("Insufficient buffer memory\n");
goto error;
}
sovc_data.h264_buffer_num = req.count;
sovc_data.h264_buffers = (SoV4l2Buffer*)calloc (sovc_data.h264_buffer_num, sizeof(SoV4l2Buffer));
if (!sovc_data.h264_buffers)
{
printf("Out of memory\n");
goto error;
}
for (i = 0; i < sovc_data.h264_buffer_num; ++i)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_QUERYBUF, &buf))
{
printf("VIDIOC_QUERYBUF\n");
goto error;
}
sovc_data.h264_buffers[i].length = buf.length;
sovc_data.h264_buffers[i].start = mmap (NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
sovc_data.h264_fd, buf.m.offset);
if (MAP_FAILED == sovc_data.h264_buffers[i].start)
{
sovc_data.h264_buffers[i].length = 0;
printf("mmap failed\n");
goto error;
}
}
for (i = 0; i < sovc_data.h264_buffer_num; ++i)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(struct v4l2_buffer));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_QBUF, &buf))
{
printf("VIDIOC_QBUF failed\n");
goto error;
}
}
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl (sovc_data.h264_fd, VIDIOC_STREAMON, &type))
{
printf("VIDIOC_STREAMON failed\n");
goto error;
}
sovc_data.fn_capture = fn_capture;
sovc_data.fn_error = fn_error;
// create the capture thread
{
pthread_t video_capture_id;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&video_capture_id,NULL,video_capture_thread,NULL);
}
printf("create video capture thread end\n");
return SOERR_NONE;
error:
if(sovc_data.h264_fd>0)
close(sovc_data.h264_fd);
sovc_data.h264_fd = 0;
return ret;
}
SOERR solcam_vc_stop()
{
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.status != SOLCAM_STATUS_RUNNING)
{
return SOERR_WRONG_STATUS;
}
sovc_data.status = SOLCAM_STATUS_STOPPED;
while(sovc_data.stopped == 0)
{
usleep(50000);
}
if(sovc_data.h264_fd>0)
{
printf("close video device\n");
close(sovc_data.h264_fd);
}
sovc_data.h264_fd = 0;
return SOERR_NONE;
}
SOERR solcam_vc_get_status(SOLCAM_STATUS *status)
{
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(status == NULL)
return SOERR_INVALID_PARAM;
*status = sovc_data.status;
return SOERR_NONE;
}
SOERR solcam_vc_uninit()
{
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
memset(&sovc_data, 0, sizeof(sovc_data));
return SOERR_NONE;
}
#define MAX_SUPPORT_RES 16
static VideoRes s_video_res[MAX_SUPPORT_RES];
static VideoResList svideo_res_list;
SOERR solcam_vc_list_res(VideoResList** res_list)
{
struct v4l2_frmsizeenum size;
struct v4l2_fmtdesc fmt;
SOERR ret = SOERR_FAILED_LIST_RES;
int h264_fd = 0;
if(res_list == NULL)
return SOERR_INVALID_PARAM;
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.status == SOLCAM_STATUS_RUNNING || sovc_data.h264_fd >0)
{
return SOERR_WRONG_STATUS;
}
if(!is_file_exist(sp_h264_dev))
{
printf("the sunny-webcam device is not been pluged\n");
return SOERR_NO_CAM_DEVICE;
}
h264_fd = open(sp_h264_dev, O_RDWR);
if(h264_fd <=0)
return SOERR_FAILED_LIST_RES;
memset(&svideo_res_list, 0, sizeof(VideoResList));
svideo_res_list.video_res = s_video_res;
fmt.index = 0;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if((v4l2_ioctl (h264_fd, VIDIOC_ENUM_FMT, &fmt)<0))
{
printf("the sunny webcam device file ioctl failed.\n");
goto exit;
}
memset (&size, 0, sizeof(struct v4l2_frmsizeenum));
size.index = 0;
size.pixel_format = fmt.pixelformat;
if((v4l2_ioctl (h264_fd, VIDIOC_ENUM_FRAMESIZES, &size)<0))
{
printf("can not list the frame size!\n");
goto exit;
}
if (size.type == V4L2_FRMSIZE_TYPE_DISCRETE)
{
do
{
s_video_res[size.index].width = size.discrete.width;
s_video_res[size.index].height = size.discrete.height;
printf("got discrete frame size %dx%d, format: 0x%x\n",
size.discrete.width,
size.discrete.height,
size.pixel_format);
size.index++;
svideo_res_list.num_res++;
if(size.index>=MAX_SUPPORT_RES)
{
break;
}
} while (v4l2_ioctl(h264_fd, VIDIOC_ENUM_FRAMESIZES, &size) >= 0);
ret = SOERR_NONE;
}
else
{
printf("known type!\n");
ret = SOERR_FAILED_LIST_RES;
}
exit:
if(svideo_res_list.num_res == 0)
{
return SOERR_FAILED_LIST_RES;
}
*res_list = &svideo_res_list;
if(h264_fd>0)
close(h264_fd);
return ret;
}
SOERR solcam_vc_get_brightness(int* max_value, int *min_value, int *cur_value)
{
struct v4l2_control control;
if(max_value == NULL || min_value == NULL || cur_value == NULL)
return SOERR_INVALID_PARAM;
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.h264_fd <= 0)
return SOERR_UNINITED;
control.id = V4L2_CID_BRIGHTNESS;
if (v4l2_ioctl (sovc_data.h264_fd, VIDIOC_G_CTRL, &control) < 0)
{
printf("get brightness failed!\n");
return SOERR_FAILED_GET_BRIGHTNESS;
}
*cur_value = control.value;
*max_value = 0xFF;
*min_value = 0;
return SOERR_NONE;
}
SOERR solcam_vc_set_brightness(int brightness_value)
{
struct v4l2_control control;
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.h264_fd <= 0)
return SOERR_UNINITED;
control.id = V4L2_CID_BRIGHTNESS;
control.value = brightness_value;
if (v4l2_ioctl (sovc_data.h264_fd, VIDIOC_S_CTRL, &control) < 0)
{
printf("set brightness failed!\n");
return SOERR_FAILED_SET_BRIGHTNESS;
}
return SOERR_NONE;
}
SOERR solcam_vc_get_compquality( int* max_value, int *min_value, int *cur_value)
{
if(max_value == NULL || min_value == NULL || cur_value == NULL)
return SOERR_INVALID_PARAM;
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.h264_fd <= 0)
return SOERR_UNINITED;
if (v4l2_ioctl (sovc_data.h264_fd, VIDIOC_G_COMPQUALITY, cur_value) < 0)
{
printf("get compquality failed!\n");
return SOERR_FAILED_GET_COMPQUALITY;
}
*max_value = 10000;
*min_value = 0;
return SOERR_NONE;
}
SOERR solcam_vc_set_compquality( int quality_value)
{
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.h264_fd <= 0)
return SOERR_UNINITED;
if (v4l2_ioctl (sovc_data.h264_fd, VIDIOC_S_COMPQUALITY, &quality_value) < 0)
{
printf("set compquality failed!\n");
return SOERR_FAILED_SET_COMPQUALITY;
}
return SOERR_NONE;
}
SOERR solcam_vc_get_cur_framerate( double *cur_value)
{
if(cur_value == NULL)
return SOERR_INVALID_PARAM;
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.status != SOLCAM_STATUS_RUNNING)
{
return SOERR_WRONG_STATUS;
}
*cur_value = sovc_data.cur_framerate;
return SOERR_NONE;
}
SOERR solcam_vc_get_cur_bitrate(int* cur_value)
{
if(cur_value == NULL)
return SOERR_INVALID_PARAM;
if(sovc_data.init_id != INIT_ID)
return SOERR_UNINITED;
if(sovc_data.status != SOLCAM_STATUS_RUNNING)
{
return SOERR_WRONG_STATUS;
}
*cur_value = sovc_data.cur_bitrate*8/1024;
return SOERR_NONE;
}
(carlsonlee_freec) |