📄 tvi_v4l2.c
字号:
/*** Video 4 Linux 2 input**** This file is part of MPlayer, see http://mplayerhq.hu/ for info. **** (c) 2003 Martin Olschewski <olschewski@zpr.uni-koeln.de>** (c) 2003 Jindrich Makovicka <makovick@kmlinux.fjfi.cvut.cz>** ** File licensed under the GPL, see http://www.fsf.org/ for more info.**** Some ideas are based on works from** Alex Beregszaszi <alex@naxine.org>** Gerd Knorr <kraxel@bytesex.org>**** CODE IS UNDER DEVELOPMENT, NO FEATURE REQUESTS PLEASE!*//*known issues:- norm setting isn't consistent with tvi_v4l- the same for volume/bass/treble/balance*/#include "config.h"#if defined(USE_TV) && defined(HAVE_TV_V4L2)#include <errno.h>#include <fcntl.h>#include <pthread.h>#include <stdio.h>#include <string.h>#include <sys/ioctl.h>#include <sys/mman.h>#include <sys/time.h>#include <sys/types.h>#include <unistd.h>#ifdef HAVE_SYS_SYSINFO_H#include <sys/sysinfo.h>#endif#include "videodev2.h"#include "../mp_msg.h"#include "../libvo/img_format.h"#include "../libaf/af_format.h"#include "tv.h"#include "audio_in.h"/* information about this file */static tvi_info_t info = { "Video 4 Linux 2 input", "v4l2", "Martin Olschewski <olschewski@zpr.uni-koeln.de>", "first try, more to come ;-)"};struct map { struct v4l2_buffer buf; void *addr; size_t len;};#define BUFFER_COUNT 6/* private data */typedef struct { /* video */ char *video_dev; int video_fd; int mp_format; struct v4l2_capability capability; struct v4l2_input input; struct v4l2_format format; struct v4l2_standard standard; struct v4l2_tuner tuner; struct map *map; int mapcount; int frames; volatile long long first_frame; long long curr_frame; /* audio video interleaving ;-) */ volatile int streamon; pthread_t audio_grabber_thread; pthread_mutex_t skew_mutex; /* 2nd level video buffers */ int first; int immediate_mode; int video_buffer_size_max; volatile int video_buffer_size_current; unsigned char **video_ringbuffer; long long *video_timebuffer; volatile int video_head; volatile int video_tail; volatile int video_cnt; pthread_t video_grabber_thread; pthread_mutex_t video_buffer_mutex; /* audio */ char *audio_dev; audio_in_t audio_in; long long audio_start_time; int audio_buffer_size; int aud_skew_cnt; unsigned char *audio_ringbuffer; long long *audio_skew_buffer; long long *audio_skew_delta_buffer; volatile int audio_head; volatile int audio_tail; volatile int audio_cnt; volatile long long audio_skew; volatile double audio_skew_factor; volatile long long audio_skew_measure_time; volatile int audio_drop; volatile int shutdown; double audio_secs_per_block; long long audio_skew_total; long long audio_skew_delta_total; long audio_recv_blocks_total; long audio_sent_blocks_total;} priv_t;#include "tvi_def.h"static void *audio_grabber(void *data);static void *video_grabber(void *data);/**********************************************************************\ Only few of the fourccs are the same in v4l2 and mplayer: IMGFMT_YVU9 == V4L2_PIX_FMT_YVU410 IMGFMT_YV12 == V4L2_PIX_FMT_YVU420 IMGFMT_NV12 == V4L2_PIX_FMT_NV12 IMGFMT_422P == V4L2_PIX_FMT_YUV422P IMGFMT_411P == V4L2_PIX_FMT_YUV411P IMGFMT_UYVY == V4L2_PIX_FMT_UYVY IMGFMT_Y41P == V4L2_PIX_FMT_Y41P This may be an useful translation table for some others: IMGFMT_RGB8 == V4L2_PIX_FMT_RGB332 IMGFMT_RGB15 == V4L2_PIX_FMT_RGB555 IMGFMT_RGB16 == V4L2_PIX_FMT_RGB565 IMGFMT_RGB24 == V4L2_PIX_FMT_RGB24 IMGFMT_RGB32 == V4L2_PIX_FMT_RGB32 IMGFMT_BGR24 == V4L2_PIX_FMT_BGR24 IMGFMT_BGR32 == V4L2_PIX_FMT_BGR32 IMGFMT_Y800 == V4L2_PIX_FMT_GREY IMGFMT_IF09 == V4L2_PIX_FMT_YUV410 IMGFMT_I420 == V4L2_PIX_FMT_YUV420 IMGFMT_YUY2 == V4L2_PIX_FMT_YUYV\**********************************************************************//*** Translate a mplayer fourcc to a video4linux2 pixel format.*/static int fcc_mp2vl(int fcc){ switch (fcc) { case IMGFMT_RGB8: return V4L2_PIX_FMT_RGB332; case IMGFMT_RGB15: return V4L2_PIX_FMT_RGB555; case IMGFMT_RGB16: return V4L2_PIX_FMT_RGB565; case IMGFMT_RGB24: return V4L2_PIX_FMT_RGB24; case IMGFMT_RGB32: return V4L2_PIX_FMT_RGB32; case IMGFMT_BGR24: return V4L2_PIX_FMT_BGR24; case IMGFMT_BGR32: return V4L2_PIX_FMT_BGR32; case IMGFMT_Y800: return V4L2_PIX_FMT_GREY; case IMGFMT_IF09: return V4L2_PIX_FMT_YUV410; case IMGFMT_I420: return V4L2_PIX_FMT_YUV420; case IMGFMT_YUY2: return V4L2_PIX_FMT_YUYV; case IMGFMT_YV12: return V4L2_PIX_FMT_YUV420; case IMGFMT_UYVY: return V4L2_PIX_FMT_UYVY; } return fcc;}/*** Translate a video4linux2 fourcc aka pixel format to mplayer.*/static int fcc_vl2mp(int fcc){ switch (fcc) { case V4L2_PIX_FMT_RGB332: return IMGFMT_RGB8; case V4L2_PIX_FMT_RGB555: return IMGFMT_RGB15; case V4L2_PIX_FMT_RGB565: return IMGFMT_RGB16; case V4L2_PIX_FMT_RGB24: return IMGFMT_RGB24; case V4L2_PIX_FMT_RGB32: return IMGFMT_RGB32; case V4L2_PIX_FMT_BGR24: return IMGFMT_BGR24; case V4L2_PIX_FMT_BGR32: return IMGFMT_BGR32; case V4L2_PIX_FMT_GREY: return IMGFMT_Y800; case V4L2_PIX_FMT_YUV410: return IMGFMT_IF09; case V4L2_PIX_FMT_YUV420: return IMGFMT_I420; case V4L2_PIX_FMT_YUYV: return IMGFMT_YUY2; case V4L2_PIX_FMT_UYVY: return IMGFMT_UYVY; } return fcc;}/*** Translate a video4linux2 fourcc aka pixel format** to a human readable string.*/static char *pixfmt2name(int pixfmt){ static char unknown[24]; switch (pixfmt) { case V4L2_PIX_FMT_RGB332: return "RGB332"; case V4L2_PIX_FMT_RGB555: return "RGB555"; case V4L2_PIX_FMT_RGB565: return "RGB565"; case V4L2_PIX_FMT_RGB555X: return "RGB555X"; case V4L2_PIX_FMT_RGB565X: return "RGB565X"; case V4L2_PIX_FMT_BGR24: return "BGR24"; case V4L2_PIX_FMT_RGB24: return "RGB24"; case V4L2_PIX_FMT_BGR32: return "BGR32"; case V4L2_PIX_FMT_RGB32: return "RGB32"; case V4L2_PIX_FMT_GREY: return "GREY"; case V4L2_PIX_FMT_YVU410: return "YVU410"; case V4L2_PIX_FMT_YVU420: return "YVU420"; case V4L2_PIX_FMT_YUYV: return "YUYV"; case V4L2_PIX_FMT_UYVY: return "UYVY";/* case V4L2_PIX_FMT_YVU422P: return "YVU422P"; *//* case V4L2_PIX_FMT_YVU411P: return "YVU411P"; */ case V4L2_PIX_FMT_YUV422P: return "YUV422P"; case V4L2_PIX_FMT_YUV411P: return "YUV411P"; case V4L2_PIX_FMT_Y41P: return "Y41P"; case V4L2_PIX_FMT_NV12: return "NV12"; case V4L2_PIX_FMT_NV21: return "NV21"; case V4L2_PIX_FMT_YUV410: return "YUV410"; case V4L2_PIX_FMT_YUV420: return "YUV420"; case V4L2_PIX_FMT_YYUV: return "YYUV"; case V4L2_PIX_FMT_HI240: return "HI240"; case V4L2_PIX_FMT_WNVA: return "WNVA"; } sprintf(unknown, "unknown (0x%x)", pixfmt); return unknown;}/*** Gives the depth of a video4linux2 fourcc aka pixel format in bits.*/static int pixfmt2depth(int pixfmt){ switch (pixfmt) { case V4L2_PIX_FMT_RGB332: return 8; case V4L2_PIX_FMT_RGB555: case V4L2_PIX_FMT_RGB565: case V4L2_PIX_FMT_RGB555X: case V4L2_PIX_FMT_RGB565X: return 16; case V4L2_PIX_FMT_BGR24: case V4L2_PIX_FMT_RGB24: return 24; case V4L2_PIX_FMT_BGR32: case V4L2_PIX_FMT_RGB32: return 32; case V4L2_PIX_FMT_GREY: return 8; case V4L2_PIX_FMT_YVU410: return 9; case V4L2_PIX_FMT_YVU420: return 12; case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: case V4L2_PIX_FMT_YUV422P: case V4L2_PIX_FMT_YUV411P: return 16; case V4L2_PIX_FMT_Y41P: case V4L2_PIX_FMT_NV12: case V4L2_PIX_FMT_NV21: return 12; case V4L2_PIX_FMT_YUV410: return 9; case V4L2_PIX_FMT_YUV420: return 12; case V4L2_PIX_FMT_YYUV: return 16; case V4L2_PIX_FMT_HI240: return 8; } return 0;}static int amode2v4l(int amode) { switch (amode) { case 0: return V4L2_TUNER_MODE_MONO; case 1: return V4L2_TUNER_MODE_STEREO; case 2: return V4L2_TUNER_MODE_LANG1; case 3: return V4L2_TUNER_MODE_LANG2; default: return -1; }}// sets and sanitizes audio buffer/block sizesstatic void setup_audio_buffer_sizes(priv_t *priv){ int bytes_per_sample = priv->audio_in.bytes_per_sample; double fps = priv->standard.frameperiod.denominator / priv->standard.frameperiod.numerator; int seconds = priv->video_buffer_size_max/fps; if (seconds < 5) seconds = 5; if (seconds > 500) seconds = 500; // make the audio buffer at least as the video buffer capacity (or 5 seconds) long priv->audio_buffer_size = 1 + seconds*priv->audio_in.samplerate *priv->audio_in.channels *bytes_per_sample/priv->audio_in.blocksize; if (priv->audio_buffer_size < 256) priv->audio_buffer_size = 256; // make the skew buffer at least 1 second long priv->aud_skew_cnt = 1 + 1*priv->audio_in.samplerate *priv->audio_in.channels *bytes_per_sample/priv->audio_in.blocksize; if (priv->aud_skew_cnt < 16) priv->aud_skew_cnt = 16; mp_msg(MSGT_TV, MSGL_V, "Audio capture - buffer %d blocks of %d bytes, skew average from %d meas.\n", priv->audio_buffer_size, priv->audio_in.blocksize, priv->aud_skew_cnt);}#if 0/*** the number of milliseconds elapsed between time0 and time1*/static size_t difftv(struct timeval time1, struct timeval time0){ return (time1.tv_sec - time0.tv_sec) * 1000 + (time1.tv_usec - time0.tv_usec) / 1000;}#endif/*** Get current video capture format.*/static int getfmt(priv_t *priv){ int i; priv->format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if ((i = ioctl(priv->video_fd, VIDIOC_G_FMT, &priv->format)) < 0) { mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get format failed: %s\n", info.short_name, strerror(errno)); } return i;}/*** Get current video capture standard.*/static int getstd(priv_t *priv){ v4l2_std_id id; int i=0; if (ioctl(priv->video_fd, VIDIOC_G_STD, &id) < 0) { mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl get standard failed: %s\n", info.short_name, strerror(errno)); return -1; } do { priv->standard.index = i++; if (ioctl(priv->video_fd, VIDIOC_ENUMSTD, &priv->standard) < 0) { return -1; } } while (priv->standard.id != id); return 0;}/***********************************************************************\ * * * * * Interface to mplayer * * * * *\***********************************************************************/static int set_mute(priv_t *priv, int value) { struct v4l2_control control; control.id = V4L2_CID_AUDIO_MUTE; control.value = value; if (ioctl(priv->video_fd, VIDIOC_S_CTRL, &control) < 0) { mp_msg(MSGT_TV,MSGL_ERR,"%s: ioctl set mute failed: %s\n", info.short_name, strerror(errno)); return 0; } return 1;}/*** MPlayer uses values from -100 up to 100 for controls.** Here they are scaled to what the tv card needs and applied.*/static int set_control(priv_t *priv, struct v4l2_control *control, int val_signed) { struct v4l2_queryctrl qctrl; qctrl.id = control->id; if (ioctl(priv->video_fd, VIDIOC_QUERYCTRL, &qctrl) < 0) { mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl query control failed: %s\n", info.short_name, strerror(errno)); return TVI_CONTROL_FALSE; } if (val_signed) { if (control->value < 0) { control->value = qctrl.default_value + control->value *
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -