📄 tvi_v4l.c
字号:
{ mp_msg(MSGT_TV, MSGL_ERR, "ioctl get channel failed: %s\n", strerror(errno)); return(TVI_CONTROL_FALSE); } return(TVI_CONTROL_TRUE); } case TVI_CONTROL_SPC_SET_INPUT: { struct video_channel chan; int req_chan = *(int *)arg; int i; if (req_chan >= priv->capability.channels) { mp_msg(MSGT_TV, MSGL_ERR, "Invalid input requested: %d, valid: 0-%d\n", req_chan, priv->capability.channels - 1); return(TVI_CONTROL_FALSE); } for (i = 0; i < priv->capability.channels; i++) { if (priv->channels[i].channel == req_chan) chan = priv->channels[i]; } if (ioctl(priv->video_fd, VIDIOCSCHAN, &chan) == -1) { mp_msg(MSGT_TV, MSGL_ERR, "ioctl set chan failed: %s\n", strerror(errno)); return(TVI_CONTROL_FALSE); } mp_msg(MSGT_TV, MSGL_INFO, "Using input '%s'\n", chan.name); priv->act_channel = i; /* update tuner state */// if (priv->capability.type & VID_TYPE_TUNER) if (priv->channels[priv->act_channel].flags & VIDEO_VC_TUNER) control(priv, TVI_CONTROL_TUN_GET_TUNER, 0); /* update local channel list */ control(priv, TVI_CONTROL_SPC_GET_INPUT, &req_chan); return(TVI_CONTROL_TRUE); case TVI_CONTROL_IMMEDIATE: priv->immediate_mode = 1; return(TVI_CONTROL_TRUE); }#ifdef HAVE_TV_TELETEXT case TVI_CONTROL_VBI_INIT: { void* ptr; tt_stream_props tsp; if (vbi_init(priv,*(char**)arg)!=TVI_CONTROL_TRUE) return TVI_CONTROL_FALSE; if(vbi_get_props(priv,&tsp)==TVI_CONTROL_TRUE) { ptr=&tsp; if(teletext_control(NULL,TV_VBI_CONTROL_START,&ptr)==TVI_CONTROL_TRUE) priv->priv_vbi=ptr; else priv->priv_vbi=NULL; } return TVI_CONTROL_TRUE; } default: return teletext_control(priv->priv_vbi,cmd,arg);#endif } return(TVI_CONTROL_UNKNOWN);}static int set_mute(priv_t* priv,int value){ if (!priv->capability.audios) { return 0; if(value) priv->audio[priv->audio_id].flags |=VIDEO_AUDIO_MUTE; else priv->audio[priv->audio_id].flags &= ~VIDEO_AUDIO_MUTE; } if(ioctl(priv->video_fd, VIDIOCSAUDIO, &priv->audio[priv->audio_id])<0) return 0; return 1;}// copies a video frame// for YV12 swaps the 2nd and 3rd planestatic inline void copy_frame(priv_t *priv, unsigned char *dest, unsigned char *source){ if(priv->tv_param->automute>0){ if (ioctl(priv->video_fd, VIDIOCGTUNER, &priv->tuner) >= 0) { if(priv->tv_param->automute<<8>priv->tuner.signal){ fill_blank_frame(dest,priv->bytesperline * priv->height,priv->format); set_mute(priv,1); return; } } set_mute(priv,0); } // YV12 uses VIDEO_PALETTE_YUV420P, but the planes are swapped if (priv->format == IMGFMT_YV12) { fast_memcpy(dest, source, priv->width * priv->height); fast_memcpy(dest+priv->width * priv->height*5/4, source+priv->width * priv->height, priv->width * priv->height/4); fast_memcpy(dest+priv->width * priv->height, source+priv->width * priv->height*5/4, priv->width * priv->height/4); return; } fast_memcpy(dest, source, priv->bytesperline * priv->height);}// maximum skew change, in frames#define MAX_SKEW_DELTA 0.6static void *video_grabber(void *data){#define MAXTOL (priv->nbuf) priv_t *priv = (priv_t*)data; struct timeval curtime; long long skew, prev_skew, xskew, interval, prev_interval; int frame; int i; int framecount; int tolerance; unsigned long num; /* start the capture process */ if ( priv->tv_param->mjpeg ) { mp_msg(MSGT_TV, MSGL_INFO, " MJP: gonna capture ! \n"); for (i=0; i < priv->nbuf; i++) { num = i; if (ioctl(priv->video_fd, MJPIOC_QBUF_CAPT, &num) < 0) mp_msg(MSGT_TV, MSGL_ERR, "\n MJP: ioctl MJPIOC_QBUF_CAPT b failed: %s\n", strerror(errno)); } } else { for (i=0; i < priv->nbuf; i++) { if (ioctl(priv->video_fd, VIDIOCMCAPTURE, &priv->buf[i]) == -1) mp_msg(MSGT_TV, MSGL_ERR, "\nioctl mcapture failed: %s\n", strerror(errno)); } } gettimeofday(&curtime, NULL); priv->starttime = (long long)1e6*curtime.tv_sec + curtime.tv_usec; priv->audio_skew_measure_time = 0; pthread_mutex_unlock(&priv->audio_starter); xskew = 0; skew = 0; interval = 0; prev_interval = 0; prev_skew = 0; tolerance = MAXTOL; for (framecount = 0; !priv->shutdown;) { for (i = 0; i < priv->nbuf && !priv->shutdown; i++, framecount++) { if (priv->immediate_mode) { while (priv->video_cnt == priv->video_buffer_size_max) { usleep(10000); if (priv->shutdown) { return NULL; } } } frame = i; if ( priv->tv_param->mjpeg ) { while (ioctl(priv->video_fd, MJPIOC_SYNC, &priv->buf[frame].frame) < 0 && (errno == EAGAIN || errno == EINTR)); } else { while (ioctl(priv->video_fd, VIDIOCSYNC, &priv->buf[frame].frame) < 0 && (errno == EAGAIN || errno == EINTR)); } mp_dbg(MSGT_TV, MSGL_DBG3, "\npicture sync failed\n"); gettimeofday(&curtime, NULL); if (!priv->immediate_mode) { interval = (long long)1e6*curtime.tv_sec + curtime.tv_usec - priv->starttime; } else { interval = (long long)1e6*framecount/priv->fps; } if (!priv->immediate_mode) { long long period, orig_interval; if (tolerance == 0) { if (interval - prev_interval == 0) { mp_msg(MSGT_TV, MSGL_V, "\nvideo capture thread: frame delta = 0\n"); } else if ((interval - prev_interval < (long long)0.85e6/priv->fps) || (interval - prev_interval > (long long)1.15e6/priv->fps) ) { mp_msg(MSGT_TV, MSGL_V, "\nvideo capture thread: frame delta ~ %.1lf fps\n", (double)1e6/(interval - prev_interval)); } } // correct the rate fluctuations on a small scale orig_interval = interval; period = priv->video_interval_sum/VIDEO_AVG_BUFFER_SIZE; if (interval - prev_interval > 105*period/100) { if (tolerance > 0) { mp_msg(MSGT_TV, MSGL_DBG3, "correcting timestamp\n"); interval = prev_interval + priv->video_interval_sum/VIDEO_AVG_BUFFER_SIZE; tolerance--; } else { mp_msg(MSGT_TV, MSGL_DBG3, "bad - frames were dropped\n"); tolerance = MAXTOL; } } else { if (tolerance < MAXTOL) { mp_msg(MSGT_TV, MSGL_DBG3, "fluctuation overcome\n"); } tolerance = MAXTOL; } priv->video_interval_sum -= priv->video_avg_buffer[priv->video_avg_ptr]; priv->video_avg_buffer[priv->video_avg_ptr++] = orig_interval-prev_interval; priv->video_interval_sum += orig_interval-prev_interval; if (priv->video_avg_ptr >= VIDEO_AVG_BUFFER_SIZE) priv->video_avg_ptr = 0;// fprintf(stderr, "fps: %lf\n", (double)1e6*VIDEO_AVG_BUFFER_SIZE/priv->video_interval_sum); // interpolate the skew in time pthread_mutex_lock(&priv->skew_mutex); xskew = priv->audio_skew + (interval - priv->audio_skew_measure_time)*priv->audio_skew_factor; pthread_mutex_unlock(&priv->skew_mutex); // correct extreme skew changes to avoid (especially) moving backwards in time if (xskew - prev_skew > (interval - prev_interval)*MAX_SKEW_DELTA) { skew = prev_skew + (interval - prev_interval)*MAX_SKEW_DELTA; } else if (xskew - prev_skew < -(interval - prev_interval)*MAX_SKEW_DELTA) { skew = prev_skew - (interval - prev_interval)*MAX_SKEW_DELTA; } else { skew = xskew; } } mp_msg(MSGT_TV, MSGL_DBG3, "\nfps = %lf, interval = %lf, a_skew = %f, corr_skew = %f\n", (interval != prev_interval) ? (double)1e6/(interval - prev_interval) : -1, (double)1e-6*interval, (double)1e-6*xskew, (double)1e-6*skew); mp_msg(MSGT_TV, MSGL_DBG3, "vcnt = %d, acnt = %d\n", priv->video_cnt, priv->audio_cnt); prev_skew = skew; prev_interval = interval; /* allocate a new buffer, if needed */ pthread_mutex_lock(&priv->video_buffer_mutex); if (priv->video_buffer_size_current < priv->video_buffer_size_max) { if (priv->video_cnt == priv->video_buffer_size_current) { unsigned char *newbuf = calloc(priv->bytesperline, priv->height); if (newbuf) { memmove(priv->video_ringbuffer+priv->video_tail+1, priv->video_ringbuffer+priv->video_tail, (priv->video_buffer_size_current-priv->video_tail)*sizeof(unsigned char *)); memmove(priv->video_timebuffer+priv->video_tail+1, priv->video_timebuffer+priv->video_tail, (priv->video_buffer_size_current-priv->video_tail)*sizeof(long long)); priv->video_ringbuffer[priv->video_tail] = newbuf; if ((priv->video_head >= priv->video_tail) && (priv->video_cnt > 0)) priv->video_head++; priv->video_buffer_size_current++; } } } pthread_mutex_unlock(&priv->video_buffer_mutex); if (priv->video_cnt == priv->video_buffer_size_current) { if (!priv->immediate_mode) { mp_msg(MSGT_TV, MSGL_ERR, "\nvideo buffer full - dropping frame\n"); } } else { if (priv->immediate_mode) { priv->video_timebuffer[priv->video_tail] = interval; } else { // compensate for audio skew // negative skew => there are more audio samples, increase interval // positive skew => less samples, shorten the interval priv->video_timebuffer[priv->video_tail] = interval - skew; } if ( priv->tv_param->mjpeg ) copy_frame(priv, priv->video_ringbuffer[priv->video_tail], priv->mmap+(priv->mjpeg_bufsize)*i); else copy_frame(priv, priv->video_ringbuffer[priv->video_tail], priv->mmap+priv->mbuf.offsets[frame]); priv->video_tail = (priv->video_tail+1)%priv->video_buffer_size_current; priv->video_cnt++; } if ( priv->tv_param->mjpeg ) { num = frame; if (ioctl(priv->video_fd, MJPIOC_QBUF_CAPT, &num) < 0) { mp_msg(MSGT_TV, MSGL_ERR, "\n MJP: ioctl MJPIOC_QBUF_CAPT end failed: %s\n", strerror(errno)); continue; } } else { if (ioctl(priv->video_fd, VIDIOCMCAPTURE, &priv->buf[frame]) == -1) { mp_msg(MSGT_TV, MSGL_ERR, "\nioctl mcapture failed: %s\n", strerror(errno)); continue; } } } } mp_msg(MSGT_TV, MSGL_INFO, " MJP: returning! \n"); return NULL;}static double grab_video_frame(priv_t *priv, char *buffer, int len){ double interval; if (priv->first) { pthread_create(&priv->video_grabber_thread, NULL, video_grabber, priv); priv->first = 0; } while (priv->video_cnt == 0) { usleep(10000); } pthread_mutex_lock(&priv->video_buffer_mutex); interval = (double)priv->video_timebuffer[priv->video_head]*1e-6; fast_memcpy(buffer, priv->video_ringbuffer[priv->video_head], len); priv->video_cnt--; priv->video_head = (priv->video_head+1)%priv->video_buffer_size_current; pthread_mutex_unlock(&priv->video_buffer_mutex); return interval;}static int get_video_framesize(priv_t *priv){ return(priv->bytesperline * priv->height);}static void *audio_grabber(void *data){ priv_t *priv = (priv_t*)data; struct timeval tv; int i, audio_skew_ptr = 0; long long current_time, prev_skew = 0; pthread_mutex_lock(&priv->audio_starter); audio_in_start_capture(&priv->audio_in); for (i = 0; i < priv->aud_skew_cnt; i++) priv->audio_skew_buffer[i] = 0; for (; !priv->shutdown;) { if (audio_in_read_chunk(&priv->audio_in, priv->audio_ringbuffer+priv->audio_tail*priv->audio_in.blocksize) < 0) continue; gettimeofday(&tv, NULL); priv->audio_recv_blocks_total++; current_time = (long long)1e6*tv.tv_sec +
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -