📄 tvi_v4l.c
字号:
/* Video 4 Linux input (C) Alex Beregszaszi Some ideas are based on xawtv/libng's grab-v4l.c written by Gerd Knorr <kraxel@bytesex.org> Multithreading, a/v sync and native ALSA support by Jindrich Makovicka <makovick@gmail.com> Mjpeg hardware encoding support by Ivan Szanto <szivan@freemail.hu> CODE IS UNDER DEVELOPMENT, NO FEATURE REQUESTS PLEASE!*/#include "config.h"#include <mplaylib.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <sys/ioctl.h>#include <sys/types.h>#include <sys/time.h>/* Necessary to prevent collisions between <linux/time.h> and <sys/time.h> when V4L2 is installed. */#define _LINUX_TIME_H#include <linux/videodev.h>#include <mplaylib.h>#include <sys/mman.h>#include <mplaylib.h>#include <mplaylib.h>#include <pthread.h>#ifdef HAVE_SYS_SYSINFO_H#include <sys/sysinfo.h>#endif#include "mp_msg.h"#include "libaf/af_format.h"#include "libmpcodecs/img_format.h"#include "libvo/fastmemcpy.h"#include "libvo/videodev_mjpeg.h"#include "tv.h"#include "audio_in.h"static tvi_handle_t *tvi_init_v4l(tv_param_t* tv_param);tvi_info_t tvi_info_v4l = { tvi_init_v4l, "Video 4 Linux input", "v4l", "Alex Beregszaszi", "under development"};#define PAL_WIDTH 768#define PAL_HEIGHT 576#define PAL_FPS 25#define NTSC_WIDTH 640#define NTSC_HEIGHT 480#define NTSC_FPS (30000.0/1001.0)#define MAX_AUDIO_CHANNELS 10#define VID_BUF_SIZE_IMMEDIATE 2#define VIDEO_AVG_BUFFER_SIZE 600typedef struct { /* general */ char *video_device; int video_fd; struct video_capability capability; struct video_channel *channels; int act_channel; struct video_tuner tuner; /* video */ struct video_picture picture; int format; /* output format */ int width; int height; int bytesperline; float fps; struct video_mbuf mbuf; unsigned char *mmap; struct video_mmap *buf; int nbuf; /* audio */ char *audio_device; audio_in_t audio_in; int audio_id; struct video_audio audio[MAX_AUDIO_CHANNELS]; int audio_channels[MAX_AUDIO_CHANNELS]; /* buffering stuff */ int immediate_mode; int audio_buffer_size; int aud_skew_cnt; unsigned char *audio_ringbuffer; long long *audio_skew_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; int first; int video_buffer_size_max; volatile int video_buffer_size_current; unsigned char **video_ringbuffer; long long *video_timebuffer; long long *video_avg_buffer; int video_avg_ptr; int video_interval_sum; volatile int video_head; volatile int video_tail; volatile int video_cnt; volatile int shutdown; pthread_t audio_grabber_thread; pthread_t video_grabber_thread; pthread_mutex_t audio_starter; pthread_mutex_t skew_mutex; pthread_mutex_t video_buffer_mutex; long long starttime; double audio_secs_per_block; long long audio_skew_total; long audio_recv_blocks_total; long audio_sent_blocks_total; long mjpeg_bufsize;#ifdef HAVE_TV_TELETEXT char *vbi_dev; int vbi_fd; int vbi_bufsize; int vbi_shutdown; pthread_t vbi_grabber_thread; void *priv_vbi;#endif tv_param_t *tv_param;} priv_t;#include "tvi_def.h"static const char *device_cap2name[] = { "capture", "tuner", "teletext", "overlay", "chromakey", "clipping", "frameram", "scales", "monochrome", "subcapture", "mpeg-decoder", "mpeg-encoder", "mjpeg-decoder", "mjpeg-encoder", NULL};static const char *device_palette2name[] = { "-", "grey", "hi240", "rgb16", "rgb24", "rgb32", "rgb15", "yuv422", "yuyv", "uyvy", "yuv420", "yuv411", "raw", "yuv422p", "yuv411p", "yuv420p", "yuv410p"};#define PALETTE(x) ((x < sizeof(device_palette2name)/sizeof(char*)) ? device_palette2name[x] : "UNKNOWN")static const char *norm2name(int mode){ switch (mode) { case VIDEO_MODE_PAL: return "pal"; case VIDEO_MODE_SECAM: return "secam"; case VIDEO_MODE_NTSC: return "ntsc"; case VIDEO_MODE_AUTO: return "auto"; default: return "unknown"; }};static const char *audio_mode2name(int mode){ switch (mode) { case VIDEO_SOUND_MONO: return "mono"; case VIDEO_SOUND_STEREO: return "stereo"; case VIDEO_SOUND_LANG1: return "language1"; case VIDEO_SOUND_LANG2: return "language2"; default: return "unknown"; }};static void *audio_grabber(void *data);static void *video_grabber(void *data);static int palette2depth(int palette){ switch(palette) { /* component */ case VIDEO_PALETTE_RGB555: return(15); case VIDEO_PALETTE_RGB565: return(16); case VIDEO_PALETTE_RGB24: return(24); case VIDEO_PALETTE_RGB32: return(32); /* planar */ case VIDEO_PALETTE_YUV411P: case VIDEO_PALETTE_YUV420P: case VIDEO_PALETTE_YUV410P: return(12); /* packed */ case VIDEO_PALETTE_YUV422P: case VIDEO_PALETTE_YUV422: case VIDEO_PALETTE_YUYV: case VIDEO_PALETTE_UYVY: case VIDEO_PALETTE_YUV420: case VIDEO_PALETTE_YUV411: return(16); } return(-1);}static int format2palette(int format){ switch(format) { case IMGFMT_BGR15: return(VIDEO_PALETTE_RGB555); case IMGFMT_BGR16: return(VIDEO_PALETTE_RGB565); case IMGFMT_BGR24: return(VIDEO_PALETTE_RGB24); case IMGFMT_BGR32: return(VIDEO_PALETTE_RGB32); case IMGFMT_YV12: case IMGFMT_I420: return(VIDEO_PALETTE_YUV420P); case IMGFMT_YUY2: return(VIDEO_PALETTE_YUV422); case IMGFMT_UYVY: return(VIDEO_PALETTE_UYVY); } 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; // make the audio buffer at least 5 seconds long priv->audio_buffer_size = 1 + 5*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);}static tvi_handle_t *tvi_init_v4l(tv_param_t* tv_param){ tvi_handle_t *h; priv_t *priv; h = new_handle(); if (!h) return(NULL); priv = h->priv; /* set video device name */ if (!tv_param->device) priv->video_device = strdup("/dev/video0"); else priv->video_device = strdup(tv_param->device); /* set video device name */ if (!tv_param->adevice) priv->audio_device = NULL; else { priv->audio_device = strdup(tv_param->adevice); } /* allocation failed */ if (!priv->video_device) { free_handle(h); return(NULL); } priv->tv_param=tv_param; return(h);}/* retrieves info about audio channels from the BTTV */static void init_v4l_audio(priv_t *priv){ int i; int reqmode; if (!priv->capability.audios) return; /* audio chanlist */ mp_msg(MSGT_TV, MSGL_V, " Audio devices: %d\n", priv->capability.audios); mp_msg(MSGT_TV, MSGL_V, "Video capture card reports the audio setup as follows:\n"); for (i = 0; i < priv->capability.audios; i++) { if (i >= MAX_AUDIO_CHANNELS) { mp_msg(MSGT_TV, MSGL_ERR, "no space for more audio channels (increase in source!) (%d > %d)\n", i, MAX_AUDIO_CHANNELS); i = priv->capability.audios; break; } priv->audio[i].audio = i; if (ioctl(priv->video_fd, VIDIOCGAUDIO, &priv->audio[i]) == -1) { mp_msg(MSGT_TV, MSGL_ERR, "ioctl get audio failed: %s\n", strerror(errno)); break; } /* mute all channels */ priv->audio[i].flags |= VIDEO_AUDIO_MUTE; reqmode = -1; if (priv->tv_param->amode >= 0) { switch (priv->tv_param->amode) { case 0: reqmode = VIDEO_SOUND_MONO; break; case 1: reqmode = VIDEO_SOUND_STEREO; break; case 2: reqmode = VIDEO_SOUND_LANG1; break; case 3: reqmode = VIDEO_SOUND_LANG2; break; default: mp_msg(MSGT_TV, MSGL_ERR, "Unknown audio mode requested.\n"); break; } if (reqmode >= 0) priv->audio[i].mode = reqmode; } ioctl(priv->video_fd, VIDIOCSAUDIO, &priv->audio[i]); // get the parameters back if (ioctl(priv->video_fd, VIDIOCGAUDIO, &priv->audio[i]) == -1) { mp_msg(MSGT_TV, MSGL_ERR, "ioctl get audio failed: %s\n", strerror(errno)); break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -