📄 v4l2.c
字号:
add_submodule(); add_shortcut( "v4l2c" ); set_description( N_("Video4Linux2 Compressed A/V") ); set_capability( "access", 0 ); /* use these when open as access_demux fails; VLC will use another demux */ set_callbacks( AccessOpen, AccessClose );vlc_module_end();/***************************************************************************** * Access: local prototypes *****************************************************************************/static void CommonClose( vlc_object_t *, demux_sys_t * );static void ParseMRL( demux_sys_t *, char *, vlc_object_t * );static void GetV4L2Params( demux_sys_t *, vlc_object_t * );static void SetAvailControlsByString( vlc_object_t *, demux_sys_t *, int );static int DemuxControl( demux_t *, int, va_list );static int AccessControl( access_t *, int, va_list );static int Demux( demux_t * );static ssize_t AccessRead( access_t *, uint8_t *, size_t );static block_t* GrabVideo( demux_t *p_demux );static block_t* ProcessVideoFrame( demux_t *p_demux, uint8_t *p_frame, size_t );static block_t* GrabAudio( demux_t *p_demux );static bool IsPixelFormatSupported( demux_t *p_demux, unsigned int i_pixelformat );#ifdef HAVE_ALSAstatic char* ResolveALSADeviceName( const char *psz_device );#endifstatic int OpenVideoDev( vlc_object_t *, demux_sys_t *, bool );static int OpenAudioDev( vlc_object_t *, demux_sys_t *, bool );static bool ProbeVideoDev( vlc_object_t *, demux_sys_t *, char *psz_device );static bool ProbeAudioDev( vlc_object_t *, demux_sys_t *, char *psz_device );static int ControlList( vlc_object_t *, int , bool, bool );static int Control( vlc_object_t *, int i_fd, const char *psz_name, int i_cid, int i_value );static int DemuxControlCallback( vlc_object_t *p_this, const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data );static int DemuxControlResetCallback( vlc_object_t *p_this, const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data );static int AccessControlCallback( vlc_object_t *p_this, const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data );static int AccessControlResetCallback( vlc_object_t *p_this, const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data );static const struct{ unsigned int i_v4l2; int i_fourcc; int i_rmask; int i_gmask; int i_bmask;} v4l2chroma_to_fourcc[] ={ /* Raw data types */ { V4L2_PIX_FMT_GREY, VLC_FOURCC('G','R','E','Y'), 0, 0, 0 }, { V4L2_PIX_FMT_HI240, VLC_FOURCC('I','2','4','0'), 0, 0, 0 }, { V4L2_PIX_FMT_RGB555, VLC_FOURCC('R','V','1','5'), 0x001f,0x03e0,0x7c00 }, { V4L2_PIX_FMT_RGB565, VLC_FOURCC('R','V','1','6'), 0x001f,0x07e0,0xf800 }, /* Won't work since we don't know how to handle such gmask values * correctly { V4L2_PIX_FMT_RGB555X, VLC_FOURCC('R','V','1','5'), 0x007c,0xe003,0x1f00 }, { V4L2_PIX_FMT_RGB565X, VLC_FOURCC('R','V','1','6'), 0x00f8,0xe007,0x1f00 }, */ { V4L2_PIX_FMT_BGR24, VLC_FOURCC('R','V','2','4'), 0xff0000,0xff00,0xff }, { V4L2_PIX_FMT_RGB24, VLC_FOURCC('R','V','2','4'), 0xff,0xff00,0xff0000 }, { V4L2_PIX_FMT_BGR32, VLC_FOURCC('R','V','3','2'), 0xff0000,0xff00,0xff }, { V4L2_PIX_FMT_RGB32, VLC_FOURCC('R','V','3','2'), 0xff,0xff00,0xff0000 }, { V4L2_PIX_FMT_YUYV, VLC_FOURCC('Y','U','Y','2'), 0, 0, 0 }, { V4L2_PIX_FMT_YUYV, VLC_FOURCC('Y','U','Y','V'), 0, 0, 0 }, { V4L2_PIX_FMT_UYVY, VLC_FOURCC('U','Y','V','Y'), 0, 0, 0 }, { V4L2_PIX_FMT_Y41P, VLC_FOURCC('I','4','1','N'), 0, 0, 0 }, { V4L2_PIX_FMT_YUV422P, VLC_FOURCC('I','4','2','2'), 0, 0, 0 }, { V4L2_PIX_FMT_YVU420, VLC_FOURCC('Y','V','1','2'), 0, 0, 0 }, { V4L2_PIX_FMT_YUV411P, VLC_FOURCC('I','4','1','1'), 0, 0, 0 }, { V4L2_PIX_FMT_YUV410, VLC_FOURCC('I','4','1','0'), 0, 0, 0 }, /* Raw data types, not in V4L2 spec but still in videodev2.h and supported * by VLC */ { V4L2_PIX_FMT_YUV420, VLC_FOURCC('I','4','2','0'), 0, 0, 0 }, /* FIXME { V4L2_PIX_FMT_RGB444, VLC_FOURCC('R','V','3','2') }, */ /* Compressed data types */ { V4L2_PIX_FMT_MJPEG, VLC_FOURCC('M','J','P','G'), 0, 0, 0 },#if 0 { V4L2_PIX_FMT_JPEG, VLC_FOURCC('J','P','E','G') }, { V4L2_PIX_FMT_DV, VLC_FOURCC('?','?','?','?') }, { V4L2_PIX_FMT_MPEG, VLC_FOURCC('?','?','?','?') },#endif { 0, 0, 0, 0, 0 }};/** * List of V4L2 chromas were confident enough to use as fallbacks if the * user hasn't provided a --v4l2-chroma value. * * Try YUV chromas first, then RGB little endian and MJPEG as last resort. */static const __u32 p_chroma_fallbacks[] ={ V4L2_PIX_FMT_YUV420, V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV422P, V4L2_PIX_FMT_YUYV, V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_BGR24, V4L2_PIX_FMT_BGR32, V4L2_PIX_FMT_MJPEG };static const struct{ const char *psz_name; unsigned int i_cid;} controls[] ={ { "brightness", V4L2_CID_BRIGHTNESS }, { "contrast", V4L2_CID_CONTRAST }, { "saturation", V4L2_CID_SATURATION }, { "hue", V4L2_CID_HUE }, { "audio-volume", V4L2_CID_AUDIO_VOLUME }, { "audio-balance", V4L2_CID_AUDIO_BALANCE }, { "audio-bass", V4L2_CID_AUDIO_BASS }, { "audio-treble", V4L2_CID_AUDIO_TREBLE }, { "audio-mute", V4L2_CID_AUDIO_MUTE }, { "audio-loudness", V4L2_CID_AUDIO_LOUDNESS }, { "black-level", V4L2_CID_BLACK_LEVEL }, { "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE }, { "do-white-balance", V4L2_CID_DO_WHITE_BALANCE }, { "red-balance", V4L2_CID_RED_BALANCE }, { "blue-balance", V4L2_CID_BLUE_BALANCE }, { "gamma", V4L2_CID_GAMMA }, { "exposure", V4L2_CID_EXPOSURE }, { "autogain", V4L2_CID_AUTOGAIN }, { "gain", V4L2_CID_GAIN }, { "hflip", V4L2_CID_HFLIP }, { "vflip", V4L2_CID_VFLIP }, { "hcenter", V4L2_CID_HCENTER }, { "vcenter", V4L2_CID_VCENTER }, { NULL, 0 }};struct buffer_t{ void * start; size_t length;};struct demux_sys_t{ char *psz_device; /* Main device from MRL, can be video or audio */ char *psz_vdev; int i_fd_video; char *psz_adev; int i_fd_audio; char *psz_requested_chroma; /* Video */ io_method io; int i_pts; struct v4l2_capability dev_cap; int i_input; struct v4l2_input *p_inputs; int i_selected_input; int i_standard; struct v4l2_standard *p_standards; v4l2_std_id i_selected_standard_id; int i_audio; /* V4L2 devices cannot have more than 32 audio inputs */ struct v4l2_audio p_audios[32]; int i_selected_audio_input; int i_tuner; struct v4l2_tuner *p_tuners; int i_codec; struct v4l2_fmtdesc *p_codecs; struct buffer_t *p_buffers; unsigned int i_nbuffers; int i_width; int i_height; float f_fps; /* <= 0.0 mean to grab at full rate */ mtime_t i_video_pts; /* only used when f_fps > 0 */ int i_fourcc; es_out_id_t *p_es_video; /* Audio */ unsigned int i_sample_rate; bool b_stereo; size_t i_audio_max_frame_size; block_t *p_block_audio; es_out_id_t *p_es_audio; int i_audio_method;#ifdef HAVE_ALSA /* ALSA Audio */ snd_pcm_t *p_alsa_pcm; size_t i_alsa_frame_size; int i_alsa_chunk_size;#endif /* Tuner */ int i_cur_tuner; int i_frequency; int i_audio_mode; /* Controls */ char *psz_set_ctrls;};static int FindMainDevice( vlc_object_t *p_this, demux_sys_t *p_sys, int i_flags, bool b_demux, bool b_forced ){ /* Find main device (video or audio) */ if( p_sys->psz_device && *p_sys->psz_device ) { msg_Dbg( p_this, "main device='%s'", p_sys->psz_device ); bool b_maindevice_is_video = false; /* Try to open as video device */ if( i_flags & FIND_VIDEO ) { msg_Dbg( p_this, "trying device '%s' as video", p_sys->psz_device ); if( ProbeVideoDev( p_this, p_sys, p_sys->psz_device ) ) { msg_Dbg( p_this, "'%s' is a video device", p_sys->psz_device ); /* Device was a video device */ free( p_sys->psz_vdev ); p_sys->psz_vdev = p_sys->psz_device; p_sys->psz_device = NULL; p_sys->i_fd_video = OpenVideoDev( p_this, p_sys, b_demux ); if( p_sys->i_fd_video < 0 ) return VLC_EGENERIC; b_maindevice_is_video = true; /* If successful we carry on to try the audio if access is forced */ } } /* Try to open as audio device only if main device was not detected as video above */ if( i_flags & FIND_AUDIO && !b_maindevice_is_video ) { msg_Dbg( p_this, "trying device '%s' as audio", p_sys->psz_device ); if( ProbeAudioDev( p_this, p_sys, p_sys->psz_device ) ) { msg_Dbg( p_this, "'%s' is an audio device", p_sys->psz_device ); /* Device was an audio device */ free( p_sys->psz_adev ); p_sys->psz_adev = p_sys->psz_device; p_sys->psz_device = NULL; p_sys->i_fd_audio = OpenAudioDev( p_this, p_sys, b_demux ); if( p_sys->i_fd_audio < 0 ) return VLC_EGENERIC; /* If successful we carry on to try the video if access is forced */ } } } /* If no device opened, only continue if the access was forced */ if( b_forced == false && !( ( i_flags & FIND_VIDEO && p_sys->i_fd_video >= 0 ) || ( i_flags & FIND_AUDIO && p_sys->i_fd_audio >= 0 ) ) ) { return VLC_EGENERIC; } /* Find video device */ if( i_flags & FIND_VIDEO && p_sys->i_fd_video < 0 ) { if( !p_sys->psz_vdev || !*p_sys->psz_vdev ) { free( p_sys->psz_vdev ); p_sys->psz_vdev = var_CreateGetString( p_this, "v4l2-dev" ); } msg_Dbg( p_this, "opening '%s' as video", p_sys->psz_vdev ); if( p_sys->psz_vdev && *p_sys->psz_vdev && ProbeVideoDev( p_this, p_sys, p_sys->psz_vdev ) ) { p_sys->i_fd_video = OpenVideoDev( p_this, p_sys, b_demux ); } } /* Find audio device */ if( i_flags & FIND_AUDIO && p_sys->i_fd_audio < 0 ) { if( !p_sys->psz_adev ) { p_sys->psz_adev = var_CreateGetNonEmptyString( p_this, "v4l2-adev" ); } msg_Dbg( p_this, "opening '%s' as audio", p_sys->psz_adev ); if( ProbeAudioDev( p_this, p_sys, p_sys->psz_adev ) ) { p_sys->i_fd_audio = OpenAudioDev( p_this, p_sys, b_demux ); } } if( !( ( i_flags & FIND_VIDEO && p_sys->i_fd_video >= 0 ) || ( i_flags & FIND_AUDIO && p_sys->i_fd_audio >= 0 ) ) ) { return VLC_EGENERIC; } return VLC_SUCCESS;}/***************************************************************************** * DemuxOpen: opens v4l2 device, access_demux callback ***************************************************************************** * * url: <video device>:::: * *****************************************************************************/static int DemuxOpen( vlc_object_t *p_this ){ demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; /* Only when selected */ if( *p_demux->psz_access == '\0' ) return VLC_EGENERIC; /* Set up p_demux */ p_demux->pf_control = DemuxControl; p_demux->pf_demux = Demux; p_demux->info.i_update = 0; p_demux->info.i_title = 0; p_demux->info.i_seekpoint = 0; p_demux->p_sys = p_sys = calloc( 1, sizeof( demux_sys_t ) ); if( p_sys == NULL ) return VLC_ENOMEM; GetV4L2Params(p_sys, (vlc_object_t *) p_demux); ParseMRL( p_sys, p_demux->psz_path, (vlc_object_t *) p_demux );#ifdef HAVE_ALSA /* Alsa support available? */ msg_Dbg( p_demux, "ALSA input support available" );#endif if( FindMainDevice( p_this, p_sys, FIND_VIDEO|FIND_AUDIO, true, !strncmp( p_demux->psz_access, "v4l2", 4 ) ) != VLC_SUCCESS ) { DemuxClose( p_this ); return VLC_EGENERIC; } return VLC_SUCCESS;}/***************************************************************************** * GetV4L2Params: fill in p_sys parameters (shared by DemuxOpen and AccessOpen) *****************************************************************************/static void GetV4L2Params( demux_sys_t *p_sys, vlc_object_t *p_obj ){ p_sys->i_video_pts = -1; p_sys->i_selected_standard_id =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -