📄 v4l.c
字号:
access_sys_t *p_sys = p_input->p_access_data; p_sys->vid_mmap.frame = ( p_sys->i_frame_pos + 1 ) % p_sys->vid_mbuf.frames; for( ;; ) { if( ioctl( p_sys->fd_video, VIDIOCMCAPTURE, &p_sys->vid_mmap ) >= 0 ) { break; } if( errno != EAGAIN ) { msg_Err( p_input, "failed while grabbing new frame" ); return( NULL ); } msg_Dbg( p_input, "another try ?" ); } //msg_Warn( p_input, "grab a new frame" ); while( ioctl(p_sys->fd_video, VIDIOCSYNC, &p_sys->i_frame_pos) < 0 && ( errno == EAGAIN || errno == EINTR ) ); p_sys->i_frame_pos = p_sys->vid_mmap.frame; /* leave i_video_frame_size alone */ return p_sys->p_video_mmap + p_sys->vid_mbuf.offsets[p_sys->i_frame_pos];}static uint8_t *GrabMJPEG( input_thread_t *p_input ){ access_sys_t *p_sys = p_input->p_access_data; struct mjpeg_sync sync; uint8_t *p_frame, *p_field, *p; uint16_t tag; uint32_t i_size; struct quicktime_mjpeg_app1 *p_app1 = NULL; /* re-queue the last frame we sync'd */ if( p_sys->i_frame_pos != -1 ) while( ioctl( p_sys->fd_video, MJPIOC_QBUF_CAPT, &p_sys->i_frame_pos ) < 0 && ( errno == EAGAIN || errno == EINTR ) ); /* sync on the next frame */ while( ioctl( p_sys->fd_video, MJPIOC_SYNC, &sync ) < 0 && ( errno == EAGAIN || errno == EINTR ) ); p_sys->i_frame_pos = sync.frame; p_frame = p_sys->p_video_mmap + p_sys->mjpeg_buffers.size * sync.frame; /* p_frame now points to the data. fix up the Quicktime APP1 marker */ tag = 0xffd9; tag = hton16( tag ); p_field = p_frame; /* look for EOI */ p = memmem( p_field, sync.length, &tag, 2 ); if( p ) { p += 2; /* data immediately following EOI */ /* UNALIGNED! */ p_app1 = (struct quicktime_mjpeg_app1 *)(p_field + 6); i_size = ((uint32_t)(p - p_field)); i_size = hton32( i_size ); memcpy( &p_app1->i_field_size, &i_size, 4 ); while( *p == 0xff && *(p+1) == 0xff ) p++; i_size = ((uint32_t)(p - p_field)); i_size = hton32( i_size ); memcpy( &p_app1->i_padded_field_size, &i_size, 4 ); } tag = 0xffd8; tag = hton16( tag ); p_field = memmem( p, sync.length - (size_t)(p - p_frame), &tag, 2 ); if( p_field ) { i_size = (uint32_t)(p_field - p_frame); i_size = hton32( i_size ); memcpy( &p_app1->i_next_field, &i_size, 4 ); /* UNALIGNED! */ p_app1 = (struct quicktime_mjpeg_app1 *)(p_field + 6); tag = 0xffd9; tag = hton16( tag ); p = memmem( p_field, sync.length - (size_t)(p_field - p_frame), &tag, 2 ); if( !p ) { /* sometimes the second field doesn't have the EOI. just put it * there */ p = p_frame + sync.length; memcpy( p, &tag, 2 ); sync.length += 2; } p += 2; i_size = (uint32_t)(p - p_field); i_size = hton32( i_size ); memcpy( &p_app1->i_field_size, &i_size, 4 ); i_size = (uint32_t)(sync.length - (uint32_t)(p_field - p_frame)); i_size = hton32( i_size ); memcpy( &p_app1->i_padded_field_size, &i_size, 4 ); } p_sys->i_video_frame_size = sync.length; return p_frame;}static int GrabVideo( input_thread_t * p_input, uint8_t **pp_data, int *pi_data, mtime_t *pi_pts ){ access_sys_t *p_sys = p_input->p_access_data; uint8_t *p_frame; if( p_sys->f_fps >= 0.1 && p_sys->i_video_pts > 0 ) { mtime_t i_dur = (mtime_t)((double)1000000 / (double)p_sys->f_fps); /* Did we wait long enougth ? */ if( p_sys->i_video_pts + i_dur > mdate() ) { return VLC_ETIMEOUT; } } if( p_sys->b_mjpeg ) p_frame = GrabMJPEG( p_input ); else p_frame = GrabCapture( p_input ); if( !p_frame ) return VLC_EGENERIC; p_sys->i_video_pts = mdate(); p_sys->p_video_frame = p_frame; *pp_data = p_sys->p_video_frame; *pi_data = p_sys->i_video_frame_size; *pi_pts = p_sys->i_video_pts; return VLC_SUCCESS;}/***************************************************************************** * Read: reads from the device into PES packets. ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, otherwise the number of * bytes. *****************************************************************************/static int Read( input_thread_t * p_input, byte_t * p_buffer, size_t i_len ){ access_sys_t *p_sys = p_input->p_access_data; int i_data = 0; int i_stream; mtime_t i_pts; while( i_len > 0 ) { /* First copy header if any */ if( p_sys->i_header_pos < p_sys->i_header_size ) { int i_copy; i_copy = __MIN( p_sys->i_header_size - p_sys->i_header_pos, (int)i_len ); memcpy( p_buffer, &p_sys->p_header[p_sys->i_header_pos], i_copy ); p_sys->i_header_pos += i_copy; p_buffer += i_copy; i_len -= i_copy; i_data += i_copy; } /* then data */ if( i_len > 0 && p_sys->i_data_pos < p_sys->i_data_size ) { int i_copy; i_copy = __MIN( p_sys->i_data_size - p_sys->i_data_pos, (int)i_len ); memcpy( p_buffer, &p_sys->p_data[p_sys->i_data_pos], i_copy ); p_sys->i_data_pos += i_copy; p_buffer += i_copy; i_len -= i_copy; i_data += i_copy; } /* The caller got what he wanted */ if( i_len == 0 ) { return i_data; } /* Read no more than one frame at a time. * That kills latency, especially for encoded v4l streams */ if( p_sys->i_data_size && p_sys->i_data_pos == p_sys->i_data_size ) { p_sys->i_data_pos = 0; p_sys->i_data_size = 0; return i_data; } /* Re-fill data by grabbing audio/video */ p_sys->i_data_pos = p_sys->i_data_size = 0; /* Try grabbing audio frames first */ i_stream = p_sys->i_streams - 1; if( p_sys->fd_audio < 0 || GrabAudio( p_input, &p_sys->p_data, &p_sys->i_data_size, &i_pts ) != VLC_SUCCESS ) { int i_ret = VLC_ETIMEOUT; /* Try grabbing video frame */ i_stream = 0; if( p_sys->fd_video > 0 ) { i_ret = GrabVideo( p_input, &p_sys->p_data, &p_sys->i_data_size, &i_pts ); } /* No video or timeout */ if( i_ret == VLC_ETIMEOUT ) { /* Sleep so we do not consume all the cpu, 10ms seems * like a good value (100fps) */ msleep( 10000 ); continue; } else if( i_ret != VLC_SUCCESS ) { msg_Err( p_input, "Error during capture!" ); return -1; } } /* create pseudo header */ p_sys->i_header_size = 16; p_sys->i_header_pos = 0; SetDWBE( &p_sys->p_header[0], i_stream ); SetDWBE( &p_sys->p_header[4], p_sys->i_data_size ); SetQWBE( &p_sys->p_header[8], i_pts ); } return i_data;}/***************************************************************************** * Demux: local prototypes *****************************************************************************/struct demux_sys_t{ int i_es; es_out_id_t **es;};static int Demux ( input_thread_t * );/**************************************************************************** * DemuxOpen: ****************************************************************************/static int DemuxOpen( vlc_object_t *p_this ){ input_thread_t *p_input = (input_thread_t *)p_this; demux_sys_t *p_sys; uint8_t *p_peek; int i_es; int i; /* a little test to see if it's a v4l stream */ if( stream_Peek( p_input->s, &p_peek, 8 ) < 8 ) { msg_Warn( p_input, "v4l plugin discarded (cannot peek)" ); return VLC_EGENERIC; } if( strncmp( p_peek, ".v4l", 4 ) || ( i_es = GetDWBE( &p_peek[4] ) ) <= 0 ) { msg_Warn( p_input, "v4l plugin discarded (not a valid stream)" ); return VLC_EGENERIC; } vlc_mutex_lock( &p_input->stream.stream_lock ); if( input_InitStream( p_input, 0 ) == -1) { vlc_mutex_unlock( &p_input->stream.stream_lock ); msg_Err( p_input, "cannot init stream" ); return( VLC_EGENERIC ); } p_input->stream.i_mux_rate = 0 / 50; vlc_mutex_unlock( &p_input->stream.stream_lock ); p_input->pf_demux = Demux; p_input->pf_demux_control = demux_vaControlDefault; p_input->p_demux_data = p_sys = malloc( sizeof( demux_sys_t ) ); p_sys->i_es = 0; p_sys->es = NULL; if( stream_Peek( p_input->s, &p_peek, 8 + 20 * i_es ) < 8 + 20 * i_es ) { msg_Err( p_input, "v4l plugin discarded (cannot peek)" ); return VLC_EGENERIC; } p_peek += 8; for( i = 0; i < i_es; i++ ) { es_format_t fmt; if( !strncmp( p_peek, "auds", 4 ) ) { es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) ); fmt.audio.i_channels = GetDWBE( &p_peek[8] ); fmt.audio.i_rate = GetDWBE( &p_peek[12] ); fmt.audio.i_bitspersample = GetDWBE( &p_peek[16] ); fmt.audio.i_blockalign = fmt.audio.i_channels * fmt.audio.i_bitspersample / 8; fmt.i_bitrate = fmt.audio.i_channels * fmt.audio.i_rate * fmt.audio.i_bitspersample; msg_Dbg( p_input, "new audio es %d channels %dHz", fmt.audio.i_channels, fmt.audio.i_rate ); TAB_APPEND( p_sys->i_es, p_sys->es, es_out_Add( p_input->p_es_out, &fmt ) ); } else if( !strncmp( p_peek, "vids", 4 ) ) { es_format_Init( &fmt, VIDEO_ES, VLC_FOURCC( p_peek[4], p_peek[5], p_peek[6], p_peek[7] ) ); fmt.video.i_width = GetDWBE( &p_peek[8] ); fmt.video.i_height = GetDWBE( &p_peek[12] ); msg_Dbg( p_input, "added new video es %4.4s %dx%d", (char*)&fmt.i_codec, fmt.video.i_width, fmt.video.i_height ); TAB_APPEND( p_sys->i_es, p_sys->es, es_out_Add( p_input->p_es_out, &fmt ) ); } p_peek += 20; } /* Skip header */ stream_Read( p_input->s, NULL, 8 + 20 * i_es ); return VLC_SUCCESS;}/**************************************************************************** * DemuxClose: ****************************************************************************/static void DemuxClose( vlc_object_t *p_this ){ input_thread_t *p_input = (input_thread_t *)p_this; demux_sys_t *p_sys = p_input->p_demux_data; if( p_sys->i_es > 0 ) { free( p_sys->es ); } free( p_sys );}/**************************************************************************** * Demux: ****************************************************************************/static int Demux( input_thread_t *p_input ){ demux_sys_t *p_sys = p_input->p_demux_data; block_t *p_block; int i_es; int i_size; uint8_t *p_peek; mtime_t i_pts; if( stream_Peek( p_input->s, &p_peek, 16 ) < 16 ) { msg_Warn( p_input, "cannot peek (EOF ?)" ); return 0; } i_es = GetDWBE( &p_peek[0] ); if( i_es < 0 || i_es >= p_sys->i_es ) { msg_Err( p_input, "cannot find ES" ); return -1; } i_size = GetDWBE( &p_peek[4] ); i_pts = GetQWBE( &p_peek[8] ); if( ( p_block = stream_Block( p_input->s, 16 + i_size ) ) == NULL ) { msg_Warn( p_input, "cannot read data" ); return 0; } p_block->p_buffer += 16; p_block->i_buffer -= 16; p_block->i_dts = p_block->i_pts = i_pts + p_input->i_pts_delay; es_out_Send( p_input->p_es_out, p_sys->es[i_es], p_block ); return 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -