📄 tvi_v4l2.c
字号:
{ if (priv->audio_inited) return; if (!priv->tv_param->noaudio) {#if defined(HAVE_ALSA9) || defined(HAVE_ALSA1X) if (priv->tv_param->alsa) audio_in_init(&priv->audio_in, AUDIO_IN_ALSA); else audio_in_init(&priv->audio_in, AUDIO_IN_OSS);#else audio_in_init(&priv->audio_in, AUDIO_IN_OSS);#endif if (priv->audio_dev) { audio_in_set_device(&priv->audio_in, priv->audio_dev); } audio_in_set_samplerate(&priv->audio_in, 44100); if (priv->capability.capabilities & V4L2_CAP_TUNER) { if (priv->tuner.audmode == V4L2_TUNER_MODE_STEREO) { audio_in_set_channels(&priv->audio_in, 2); } else { audio_in_set_channels(&priv->audio_in, 1); } } else { if (priv->tv_param->forcechan >= 0) { audio_in_set_channels(&priv->audio_in, priv->tv_param->forcechan); } else { audio_in_set_channels(&priv->audio_in, 2); } } if (audio_in_setup(&priv->audio_in) < 0) return; priv->audio_inited = 1; }}#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 * (qctrl.default_value - qctrl.minimum) / 100; } else { control->value = qctrl.default_value + control->value * (qctrl.maximum - qctrl.default_value) / 100; } } else { if (control->value < 50) { control->value = qctrl.default_value + (control->value-50) * (qctrl.default_value - qctrl.minimum) / 50; } else { control->value = qctrl.default_value + (control->value-50) * (qctrl.maximum - qctrl.default_value) / 50; } } if (ioctl(priv->video_fd, VIDIOC_S_CTRL, control) < 0) { mp_msg(MSGT_TV, MSGL_ERR,"%s: ioctl set %s %d failed: %s\n", info.short_name, qctrl.name, control->value, strerror(errno)); return TVI_CONTROL_FALSE; } mp_msg(MSGT_TV, MSGL_V, "%s: set %s: %d [%d, %d]\n", info.short_name, qctrl.name, control->value, qctrl.minimum, qctrl.maximum); return TVI_CONTROL_TRUE;}/*** Scale the control values back to what mplayer needs.*/static int get_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 (ioctl(priv->video_fd, VIDIOC_G_CTRL, control) < 0) { mp_msg(MSGT_TV, MSGL_ERR,"%s: ioctl get %s failed: %s\n", info.short_name, qctrl.name, strerror(errno)); return TVI_CONTROL_FALSE; } mp_msg(MSGT_TV, MSGL_V, "%s: get %s: %d [%d, %d]\n", info.short_name, qctrl.name, control->value, qctrl.minimum, qctrl.maximum); if (val_signed) { if (control->value < qctrl.default_value) { control->value = (control->value - qctrl.default_value) * 100 / (qctrl.default_value - qctrl.minimum); } else { control->value = (control->value - qctrl.default_value) * 100 / (qctrl.maximum - qctrl.default_value); } } else { if (control->value < qctrl.default_value) { control->value = (control->value - qctrl.default_value) * 50 / (qctrl.default_value - qctrl.minimum) + 50; } else { control->value = (control->value - qctrl.default_value) * 50 / (qctrl.maximum - qctrl.default_value) + 50; } } return TVI_CONTROL_TRUE;}#ifdef HAVE_TV_TELETEXTstatic int vbi_init(priv_t* priv,char* device){ int vbi_fd=0; struct v4l2_capability cap; struct v4l2_format fmt; int res; if(!device) return TVI_CONTROL_FALSE; priv->vbi_dev=strdup(device); vbi_fd=open(priv->vbi_dev,O_RDWR); if(vbi_fd<0){ mp_msg(MSGT_TV,MSGL_ERR,"vbi: could not open device %s\n",priv->vbi_dev); return TVI_CONTROL_FALSE; } if(ioctl(vbi_fd,VIDIOC_QUERYCAP,&cap)<0){ mp_msg(MSGT_TV,MSGL_ERR,"vbi: Query capatibilities failed for %s\n",priv->vbi_dev); close(vbi_fd); return TVI_CONTROL_FALSE; } if(!cap.capabilities & V4L2_CAP_VBI_CAPTURE){ mp_msg(MSGT_TV,MSGL_ERR,"vbi: %s does not support VBI capture\n",priv->vbi_dev); close(vbi_fd); return TVI_CONTROL_FALSE; } memset(&fmt,0,sizeof(struct v4l2_format)); fmt.type=V4L2_BUF_TYPE_VBI_CAPTURE; if((res=ioctl(vbi_fd,VIDIOC_G_FMT,&fmt))<0){ mp_msg(MSGT_TV,MSGL_ERR,"vbi: Query format failed: %x\n",res); close(vbi_fd); return TVI_CONTROL_FALSE; } if(fmt.fmt.vbi.sample_format!=V4L2_PIX_FMT_GREY){ mp_msg(MSGT_TV,MSGL_ERR,"vbi: format 0x%x is not supported\n",fmt.fmt.vbi.sample_format); close(vbi_fd); return TVI_CONTROL_FALSE; } priv->vbi_fd=vbi_fd; mp_msg(MSGT_TV,MSGL_DBG3,"vbi: init ok\n"); return TVI_CONTROL_TRUE;}static int vbi_get_props(priv_t* priv,tt_stream_props* ptsp){ struct v4l2_format fmt; int res; if(!priv || !ptsp) return TVI_CONTROL_FALSE; memset(&fmt,0,sizeof(struct v4l2_format)); fmt.type=V4L2_BUF_TYPE_VBI_CAPTURE; if((res=ioctl(priv->vbi_fd,VIDIOC_G_FMT,&fmt))<0){ mp_msg(MSGT_TV,MSGL_ERR,"vbi_get_props: Query format failed: %x\n",res); return TVI_CONTROL_FALSE; } ptsp->interlaced=(fmt.fmt.vbi.flags& V4L2_VBI_INTERLACED?1:0); ptsp->offset=fmt.fmt.vbi.offset; ptsp->sampling_rate=fmt.fmt.vbi.sampling_rate; ptsp->samples_per_line=fmt.fmt.vbi.samples_per_line, ptsp->count[0]=fmt.fmt.vbi.count[0]; ptsp->count[1]=fmt.fmt.vbi.count[1]; ptsp->bufsize = ptsp->samples_per_line * (ptsp->count[0] + ptsp->count[1]); mp_msg(MSGT_TV,MSGL_V,"vbi_get_props: sampling_rate=%d,offset:%d,samples_per_line: %d\n interlaced:%s, count=[%d,%d]\n", ptsp->sampling_rate, ptsp->offset, ptsp->samples_per_line, ptsp->interlaced?"Yes":"No", ptsp->count[0], ptsp->count[1]); return TVI_CONTROL_TRUE;}static void *vbi_grabber(void *data){ priv_t *priv = (priv_t *) data; int bytes,seq,prev_seq; unsigned char* buf; tt_stream_props tsp; if(!priv->priv_vbi){ mp_msg(MSGT_TV,MSGL_WARN,"vbi: vbi not initialized. stopping thread.\n"); return NULL; } if(vbi_get_props(priv,&tsp)!=TVI_CONTROL_TRUE) return NULL; buf=malloc(tsp.bufsize); seq=0; prev_seq=0; mp_msg(MSGT_TV,MSGL_V,"vbi: vbi capture thread started.\n"); while (!priv->vbi_shutdown){ bytes=read(priv->vbi_fd,buf,tsp.bufsize); if (bytes!=tsp.bufsize){ mp_msg(MSGT_TV,MSGL_WARN,"vbi: expecting bytes: %d, got: %d\n",tsp.bufsize,bytes); break; } seq=*(int*)(buf+bytes-4); if(seq<=1) continue; if (prev_seq && seq!=prev_seq+1){ prev_seq=0; seq=0; } prev_seq=seq; teletext_control(priv->priv_vbi,TV_VBI_CONTROL_DECODE_PAGE,&buf); mp_msg(MSGT_TV,MSGL_DBG3,"grabber: seq:%d\n",seq); } free(buf); return NULL;}#endif //HAVE_TV_TELETEXTstatic int control(priv_t *priv, int cmd, void *arg){ struct v4l2_control control; struct v4l2_frequency frequency; switch(cmd) { case TVI_CONTROL_IS_VIDEO: return priv->capability.capabilities & V4L2_CAP_VIDEO_CAPTURE? TVI_CONTROL_TRUE: TVI_CONTROL_FALSE; case TVI_CONTROL_IS_AUDIO: if (priv->tv_param->force_audio) return TVI_CONTROL_TRUE; case TVI_CONTROL_IS_TUNER: return priv->capability.capabilities & V4L2_CAP_TUNER? TVI_CONTROL_TRUE: TVI_CONTROL_FALSE; case TVI_CONTROL_IMMEDIATE: priv->immediate_mode = 1; return TVI_CONTROL_TRUE; case TVI_CONTROL_VID_GET_FPS: *(float *)arg = (float)priv->standard.frameperiod.denominator / priv->standard.frameperiod.numerator; mp_msg(MSGT_TV, MSGL_V, "%s: get fps: %f\n", info.short_name, *(float *)arg); return TVI_CONTROL_TRUE; case TVI_CONTROL_VID_GET_BITS: if (getfmt(priv) < 0) return TVI_CONTROL_FALSE; *(int *)arg = pixfmt2depth(priv->format.fmt.pix.pixelformat); mp_msg(MSGT_TV, MSGL_V, "%s: get depth: %d\n", info.short_name, *(int *)arg); return TVI_CONTROL_TRUE; case TVI_CONTROL_VID_GET_FORMAT: if (getfmt(priv) < 0) return TVI_CONTROL_FALSE; *(int *)arg = fcc_vl2mp(priv->format.fmt.pix.pixelformat); mp_msg(MSGT_TV, MSGL_V, "%s: get format: %s\n", info.short_name, pixfmt2name(priv->format.fmt.pix.pixelformat)); return TVI_CONTROL_TRUE; case TVI_CONTROL_VID_SET_FORMAT: if (getfmt(priv) < 0) return TVI_CONTROL_FALSE; priv->format.fmt.pix.pixelformat = fcc_mp2vl(*(int *)arg); priv->format.fmt.pix.field = V4L2_FIELD_ANY; priv->mp_format = *(int *)arg; mp_msg(MSGT_TV, MSGL_V, "%s: set format: %s\n", info.short_name, pixfmt2name(priv->format.fmt.pix.pixelformat)); if (ioctl(priv->video_fd, VIDIOC_S_FMT, &priv->format) < 0) { mp_msg(MSGT_TV, MSGL_ERR, "%s: ioctl set format failed: %s\n", info.short_name, strerror(errno)); return TVI_CONTROL_FALSE; } /* according to the v4l2 specs VIDIOC_S_FMT should not fail, inflexible drivers might even always return the default parameters -> update the format here*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -