v4l2: Experimental ALSA input support. Currently non functional. Still defaults...
authorRichard Hosking <hovis@videolan.org>
Thu, 29 Nov 2007 16:07:22 +0000 (16:07 +0000)
committerRichard Hosking <hovis@videolan.org>
Thu, 29 Nov 2007 16:07:22 +0000 (16:07 +0000)
configure.ac
modules/access/v4l2.c

index 8c10927..7cc7b4b 100644 (file)
@@ -2347,7 +2347,7 @@ then
 fi
 
 dnl
-dnl  Video4Linux plugin
+dnl  Video4Linux2 plugin
 dnl
 AC_ARG_ENABLE(v4l2,
   [  --enable-v4l2           Video4Linux2 input support (default disabled)])
@@ -2360,6 +2360,19 @@ then
     VLC_ADD_CPPFLAGS([v4l2],[-I${with_v4l2}/include])
   fi
 
+  AC_CHECK_HEADER(alsa/asoundlib.h, AC_CHECK_LIB(asound, main, have_v4l2_alsa="true", have_v4l2_alsa="false"),have_v4l2_alsa="false")
+  if test "${have_v4l2_alsa}" = "true"
+  then
+    CFLAGS="${CFLAGS_save}"
+    AC_TRY_COMPILE([#define ALSA_PCM_NEW_HW_PARAMS_API
+                    #define ALSA_PCM_NEW_SW_PARAMS_API
+                    #include <alsa/asoundlib.h>],
+       [snd_pcm_hw_params_get_period_time(0,0,0);],
+        AC_DEFINE(HAVE_ALSA_NEW_API, 1, Define if ALSA is at least rc4))
+    VLC_ADD_LDFLAGS([v4l2],[-lasound -lm -ldl])
+    AC_DEFINE(HAVE_ALSA, 1, Define if ALSA is present.)
+  fi
+
   CPPFLAGS="${CPPFLAGS_save} ${CPPFLAGS_v4l2}"
   AC_CHECK_HEADERS(linux/videodev2.h, [
     VLC_ADD_PLUGINS([v4l2])
index eca71f8..d968960 100644 (file)
 /*
  * Sections based on the reference V4L2 capture example at
  * http://v4l2spec.bytesex.org/spec/capture-example.html
+ *
+ * ALSA support based on parts of
+ * http://www.equalarea.com/paul/alsa-audio.html
+ * and hints taken from alsa-utils (aplay/arecord)
+ * http://www.alsa-project.org
  */
 
 /*
  * TODO: No mjpeg support yet.
  * TODO: Tuner partial implementation.
- * TODO: Alsa input support?
+ * TODO: Alsa input support - experimental
  */
 
 /*****************************************************************************
 
 #include <sys/soundcard.h>
 
+#ifdef HAVE_ALSA
+# define ALSA_PCM_NEW_HW_PARAMS_API
+# define ALSA_PCM_NEW_SW_PARAMS_API
+# include <alsa/asoundlib.h>
+#endif
+
 /*****************************************************************************
  * Module descriptior
  *****************************************************************************/
@@ -102,6 +113,9 @@ static void Close( vlc_object_t * );
 #define FPS_TEXT N_( "Framerate" )
 #define FPS_LONGTEXT N_( "Framerate to capture, if applicable " \
     "(-1 for autodetect)." )
+#define ALSA_TEXT N_( "Use Alsa" )
+#define ALSA_LONGTEXT N_( \
+    "Use ALSA instead of OSS for audio" )
 #define STEREO_TEXT N_( "Stereo" )
 #define STEREO_LONGTEXT N_( \
     "Capture the audio stream in stereo." )
@@ -162,6 +176,8 @@ vlc_module_begin();
     add_integer( "v4l2-hue", -1, NULL, HUE_TEXT,
                 HUE_LONGTEXT, VLC_TRUE );
     add_float( "v4l2-fps", 0, NULL, FPS_TEXT, FPS_LONGTEXT, VLC_TRUE );
+    add_bool( "v4l2-alsa", VLC_FALSE, NULL, ALSA_TEXT, ALSA_LONGTEXT,
+                VLC_TRUE );
     add_bool( "v4l2-stereo", VLC_TRUE, NULL, STEREO_TEXT, STEREO_LONGTEXT,
                 VLC_TRUE );
     add_integer( "v4l2-samplerate", 48000, NULL, SAMPLERATE_TEXT,
@@ -189,6 +205,7 @@ static block_t* GrabAudio( demux_t *p_demux );
 
 vlc_bool_t IsPixelFormatSupported( demux_t *p_demux, unsigned int i_pixelformat );
 
+char* ResolveALSADeviceName( char *psz_device );
 static int OpenVideoDev( demux_t *, char *psz_device );
 static int OpenAudioDev( demux_t *, char *psz_device );
 static vlc_bool_t ProbeVideoDev( demux_t *, char *psz_device );
@@ -281,11 +298,19 @@ struct demux_sys_t
     es_out_id_t *p_es_video;
 
     /* Audio */
-    int i_sample_rate;
+    unsigned int i_sample_rate;
     vlc_bool_t b_stereo;
     int i_audio_max_frame_size;
     block_t *p_block_audio;
     es_out_id_t *p_es_audio;
+
+    /* ALSA Audio */
+    vlc_bool_t b_use_alsa;
+#ifdef HAVE_ALSA
+    snd_pcm_t *p_alsa_pcm;
+    int i_alsa_frame_size;
+    int i_alsa_chunk_size;
+#endif
 };
 
 /*****************************************************************************
@@ -342,6 +367,10 @@ static int Open( vlc_object_t *p_this )
 
     p_sys->psz_requested_chroma = var_CreateGetString( p_demux, "v4l2-chroma" );
 
+    var_Create( p_demux, "v4l2-alsa", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
+    var_Get( p_demux, "v4l2-alsa", &val );
+    p_sys->b_use_alsa = val.b_bool;
+
     var_Create( p_demux, "v4l2-stereo", VLC_VAR_BOOL | VLC_VAR_DOINHERIT );
     var_Get( p_demux, "v4l2-stereo", &val );
     p_sys->b_stereo = val.b_bool;
@@ -357,6 +386,11 @@ static int Open( vlc_object_t *p_this )
 
     ParseMRL( p_demux );
 
+    /* Alsa support available? */
+#ifdef HAVE_ALSA
+    msg_Dbg( p_demux, "ALSA input support available" );
+#endif
+
     /* Find main device (video or audio) */
     if( p_sys->psz_device && *p_sys->psz_device )
     {
@@ -619,6 +653,11 @@ static void ParseMRL( demux_t *p_demux )
                     strtol( psz_parser + strlen( "samplerate=" ),
                             &psz_parser, 0 );
             }
+            else if( !strncmp( psz_parser, "alsa", strlen( "alsa" ) ) )
+            {
+                psz_parser += strlen( "alsa" );
+                p_sys->b_use_alsa = VLC_TRUE;
+            }
             else if( !strncmp( psz_parser, "stereo", strlen( "stereo" ) ) )
             {
                 psz_parser += strlen( "stereo" );
@@ -730,7 +769,16 @@ static void Close( vlc_object_t *p_this )
 
     /* Close */
     if( p_sys->i_fd_video >= 0 ) close( p_sys->i_fd_video );
-    if( p_sys->i_fd_audio >= 0 ) close( p_sys->i_fd_audio );
+    if( p_sys->b_use_alsa )
+    {
+#ifdef HAVE_ALSA
+        if( p_sys->p_alsa_pcm ) snd_pcm_close( p_sys->p_alsa_pcm );
+#endif
+    }
+    else 
+    {
+        if( p_sys->i_fd_audio >= 0 ) close( p_sys->i_fd_audio );
+    }
 
     if( p_sys->p_block_audio ) block_Release( p_sys->p_block_audio );
     if( p_sys->psz_device ) free( p_sys->psz_device );
@@ -982,8 +1030,6 @@ static block_t* GrabAudio( demux_t *p_demux )
     int i_read, i_correct;
     block_t *p_block;
 
-    /* Copied from v4l.c */
-
     if( p_sys->p_block_audio ) p_block = p_sys->p_block_audio;
     else p_block = block_New( p_demux, p_sys->i_audio_max_frame_size );
 
@@ -995,8 +1041,22 @@ static block_t* GrabAudio( demux_t *p_demux )
 
     p_sys->p_block_audio = p_block;
 
-    i_read = read( p_sys->i_fd_audio, p_block->p_buffer,
-                   p_sys->i_audio_max_frame_size );
+    if( p_sys->b_use_alsa )
+    {
+        /* ALSA */
+#ifdef HAVE_ALSA
+        i_read = snd_pcm_readi( p_sys->p_alsa_pcm, p_block->p_buffer, p_sys->i_alsa_chunk_size );
+        /* TODO: ALSA ERROR HANDLING?? xrun?? */
+#else
+        i_read = 0;
+#endif
+    }
+    else
+    {
+        /* OSS */
+        i_read = read( p_sys->i_fd_audio, p_block->p_buffer,
+                    p_sys->i_audio_max_frame_size );
+    }
 
     if( i_read <= 0 ) return 0;
 
@@ -1005,9 +1065,39 @@ static block_t* GrabAudio( demux_t *p_demux )
 
     /* Correct the date because of kernel buffering */
     i_correct = i_read;
-    if( ioctl( p_sys->i_fd_audio, SNDCTL_DSP_GETISPACE, &buf_info ) == 0 )
+    if( !p_sys->b_use_alsa )
     {
-        i_correct += buf_info.bytes;
+        if( ioctl( p_sys->i_fd_audio, SNDCTL_DSP_GETISPACE, &buf_info ) == 0 )
+        {
+            i_correct += buf_info.bytes;
+        }
+    }
+    else
+    {
+#ifdef HAVE_ALSA
+        /* TODO: ALSA timing */
+        /* Very experimental code... */
+        int i_err;
+        snd_pcm_sframes_t delay = 0;
+        if( ( i_err = snd_pcm_delay( p_sys->p_alsa_pcm, &delay ) ) >= 0 )
+        {
+            int i_correction_delta = delay * p_sys->i_alsa_frame_size;
+            /* Test for overrun */
+            if( i_correction_delta>p_sys->i_audio_max_frame_size )
+            {
+                msg_Warn( p_demux, "ALSA read overrun" );
+                i_correction_delta = p_sys->i_audio_max_frame_size;
+                snd_pcm_prepare( p_sys->p_alsa_pcm );
+            }
+            i_correct += i_correction_delta;
+        }
+        else
+        {
+            /* delay failed so reset */
+            msg_Warn( p_demux, "ALSA snd_pcm_delay failed (%s)", snd_strerror( i_err ) );
+            snd_pcm_prepare( p_sys->p_alsa_pcm );
+        }
+#endif
     }
 
     /* Timestamp */
@@ -1615,55 +1705,244 @@ open_failed:
 
 }
 
+/*****************************************************************************
+ * ResolveALSADeviceName: Change any . to : in the ALSA device name
+ *****************************************************************************/
+char* ResolveALSADeviceName( char *psz_device )
+{
+    char* psz_alsa_name = strdup( psz_device );
+    for( unsigned int i = 0; i < strlen( psz_device ); i++ )
+    {
+        if( psz_alsa_name[i] == '.' ) psz_alsa_name[i] = ':';
+    }
+    return psz_alsa_name;
+}
+
 /*****************************************************************************
  * OpenAudioDev: open and set up the audio device and probe for capabilities
  *****************************************************************************/
 int OpenAudioDev( demux_t *p_demux, char *psz_device )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
-    int i_fd, i_format;
+    int i_fd = 0;
+    int i_format;
+#ifdef HAVE_ALSA
+    p_sys->p_alsa_pcm = NULL;
+    char* psz_alsa_device_name = ResolveALSADeviceName( psz_device );        
+    snd_pcm_hw_params_t *p_hw_params = NULL;
+    snd_pcm_uframes_t buffer_size;
+    snd_pcm_uframes_t chunk_size;
+#endif
 
-    if( (i_fd = open( psz_device, O_RDONLY | O_NONBLOCK )) < 0 )
+    if( p_sys->b_use_alsa ) 
     {
-        msg_Err( p_demux, "cannot open audio device (%m)" );
-        goto adev_fail;
-    }
+        /* ALSA */
 
-    i_format = AFMT_S16_LE;
-    if( ioctl( i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
-        || i_format != AFMT_S16_LE )
-    {
-        msg_Err( p_demux, "cannot set audio format (16b little endian) "
-                 "(%m)" );
-        goto adev_fail;
-    }
+#ifdef HAVE_ALSA
+        int i_err;
 
-    if( ioctl( i_fd, SNDCTL_DSP_STEREO,
-               &p_sys->b_stereo ) < 0 )
-    {
-        msg_Err( p_demux, "cannot set audio channels count (%m)" );
-        goto adev_fail;
-    }
+        if( ( i_err = snd_pcm_open( &p_sys->p_alsa_pcm, psz_alsa_device_name, 
+            SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK ) ) < 0)
+        {
+            msg_Err( p_demux, "Cannot open ALSA audio device %s (%s)", 
+                psz_alsa_device_name,
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
 
-    if( ioctl( i_fd, SNDCTL_DSP_SPEED,
-               &p_sys->i_sample_rate ) < 0 )
+        if( ( i_err = snd_pcm_nonblock( p_sys->p_alsa_pcm, 1 ) ) < 0)
+        {
+            msg_Err( p_demux, "Cannot set ALSA nonblock (%s)", 
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Begin setting hardware parameters */
+
+        if( ( i_err = snd_pcm_hw_params_malloc( &p_hw_params ) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot allocate hardware parameter structure (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        if( ( i_err = snd_pcm_hw_params_any( p_sys->p_alsa_pcm, p_hw_params ) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot initialize hardware parameter structure (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Set Interleaved access */
+        if( ( i_err = snd_pcm_hw_params_set_access( p_sys->p_alsa_pcm, p_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot set access type (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Set 16 bit little endian */
+        if( ( i_err = snd_pcm_hw_params_set_format( p_sys->p_alsa_pcm, p_hw_params, SND_PCM_FORMAT_S16_LE ) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot set sample format (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Set sample rate */
+        #ifdef HAVE_ALSA_NEW_API
+            i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, &p_sys->i_sample_rate, NULL );
+        #else
+            i_err = snd_pcm_hw_params_set_rate_near( p_sys->p_alsa_pcm, p_hw_params, p_sys->i_sample_rate, NULL );
+        #endif
+        if( i_err < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot set sample rate (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Set channels */
+        unsigned int channels = p_sys->b_stereo ? 2 : 1;
+        if( ( i_err = snd_pcm_hw_params_set_channels( p_sys->p_alsa_pcm, p_hw_params, channels ) ) < 0 )
+        {
+            channels = ( channels==1 ) ? 2 : 1;
+            msg_Warn( p_demux, "ALSA: cannot set channel count (%s). Trying with channels=%d",
+                snd_strerror( i_err ),
+                channels );                    
+            if( ( i_err = snd_pcm_hw_params_set_channels( p_sys->p_alsa_pcm, p_hw_params, channels ) ) < 0 )
+            {
+                msg_Err( p_demux, "ALSA: cannot set channel count (%s)", 
+                    snd_strerror( i_err ) );
+                goto adev_fail;
+            }
+            p_sys->b_stereo = ( channels == 2 );
+        }
+
+        /* Set metrics for buffer calculations later */
+        unsigned int buffer_time;            
+        if( ( i_err = snd_pcm_hw_params_get_buffer_time_max(p_hw_params, &buffer_time, 0) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot get buffer time max (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;           
+        }
+        if (buffer_time > 500000) buffer_time = 500000;
+
+        /* Set period time */
+        unsigned int period_time = buffer_time / 4;
+        #ifdef HAVE_ALSA_NEW_API
+            i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, &period_time, 0 );
+        #else
+            i_err = snd_pcm_hw_params_set_period_time_near( p_sys->p_alsa_pcm, p_hw_params, period_time, 0 );
+        #endif
+        if( i_err < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot set period time (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }     
+
+        /* Set buffer time */
+        #ifdef HAVE_ALSA_NEW_API
+            i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, &buffer_time, 0 );
+        #else
+            i_err = snd_pcm_hw_params_set_buffer_time_near( p_sys->p_alsa_pcm, p_hw_params, buffer_time, 0 );
+        #endif
+        if( i_err < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot set buffer time (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Apply new hardware parameters */
+        if( ( i_err = snd_pcm_hw_params( p_sys->p_alsa_pcm, p_hw_params ) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot set hw parameters (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Get various buffer metrics */
+        snd_pcm_hw_params_get_period_size( p_hw_params, &chunk_size, 0 );
+        snd_pcm_hw_params_get_buffer_size( p_hw_params, &buffer_size );
+        if (chunk_size == buffer_size)
+        {
+            msg_Err( p_demux, "ALSA: period cannot equal buffer size (%lu == %lu)",
+                chunk_size, buffer_size);
+            goto adev_fail;
+        }
+
+        int bits_per_sample = snd_pcm_format_physical_width(SND_PCM_FORMAT_S16_LE);
+        int bits_per_frame = bits_per_sample * channels;
+
+        p_sys->i_alsa_chunk_size = chunk_size;
+        p_sys->i_alsa_frame_size = (bits_per_sample / 8) * channels;
+        p_sys->i_audio_max_frame_size = chunk_size * bits_per_frame / 8; 
+
+        snd_pcm_hw_params_free( p_hw_params );
+        p_hw_params = NULL;
+
+        /* Prep device */
+        if( ( i_err = snd_pcm_prepare( p_sys->p_alsa_pcm ) ) < 0 )
+        {
+            msg_Err( p_demux, "ALSA: cannot prepare audio interface for use (%s)",
+                snd_strerror( i_err ) );
+            goto adev_fail;
+        }
+
+        /* Return a fake handle so other tests work */
+        i_fd = 1;
+
+#endif
+    } 
+    else 
     {
-        msg_Err( p_demux, "cannot set audio sample rate (%m)" );
-        goto adev_fail;
+        /* OSS */
+
+        if( (i_fd = open( psz_device, O_RDONLY | O_NONBLOCK )) < 0 )
+        {
+            msg_Err( p_demux, "cannot open OSS audio device (%m)" );
+            goto adev_fail;
+        }
+
+        i_format = AFMT_S16_LE;
+        if( ioctl( i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0
+            || i_format != AFMT_S16_LE )
+        {
+            msg_Err( p_demux, "cannot set audio format (16b little endian) "
+                     "(%m)" );
+            goto adev_fail;
+        }
+
+        if( ioctl( i_fd, SNDCTL_DSP_STEREO,
+                   &p_sys->b_stereo ) < 0 )
+        {
+            msg_Err( p_demux, "cannot set audio channels count (%m)" );
+            goto adev_fail;
+        }
+
+        if( ioctl( i_fd, SNDCTL_DSP_SPEED,
+                   &p_sys->i_sample_rate ) < 0 )
+        {
+            msg_Err( p_demux, "cannot set audio sample rate (%m)" );
+            goto adev_fail;
+        }
+
+        p_sys->i_audio_max_frame_size = 6 * 1024;
     }
 
     msg_Dbg( p_demux, "opened adev=`%s' %s %dHz",
              psz_device, p_sys->b_stereo ? "stereo" : "mono",
              p_sys->i_sample_rate );
 
-    p_sys->i_audio_max_frame_size = 6 * 1024;
-
     es_format_t fmt;
     es_format_Init( &fmt, AUDIO_ES, VLC_FOURCC('a','r','a','w') );
 
     fmt.audio.i_channels = p_sys->b_stereo ? 2 : 1;
     fmt.audio.i_rate = p_sys->i_sample_rate;
-    fmt.audio.i_bitspersample = 16; /* FIXME ? */
+    fmt.audio.i_bitspersample = 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;
 
@@ -1672,11 +1951,22 @@ int OpenAudioDev( demux_t *p_demux, char *psz_device )
 
     p_sys->p_es_audio = es_out_Add( p_demux->out, &fmt );
 
+#ifdef HAVE_ALSA
+    free( psz_alsa_device_name );
+#endif
+
     return i_fd;
 
  adev_fail:
 
     if( i_fd >= 0 ) close( i_fd );
+
+#ifdef HAVE_ALSA
+    if( p_hw_params ) snd_pcm_hw_params_free( p_hw_params );
+    if( p_sys->p_alsa_pcm ) snd_pcm_close( p_sys->p_alsa_pcm );
+    free( psz_alsa_device_name );
+#endif
+
     return -1;
 
 }
@@ -1975,21 +2265,51 @@ vlc_bool_t ProbeAudioDev( demux_t *p_demux, char *psz_device )
 {
     int i_fd = 0;
     int i_caps;
+    demux_sys_t *p_sys = p_demux->p_sys;
 
-    if( ( i_fd = open( psz_device, O_RDONLY | O_NONBLOCK ) ) < 0 )
+    if( p_sys->b_use_alsa )
     {
-        msg_Err( p_demux, "cannot open audio device (%m)" );
-        goto open_failed;
-    }
+        /* ALSA */
 
-    /* this will fail if the device is video */
-    if( ioctl( i_fd, SNDCTL_DSP_GETCAPS, &i_caps ) < 0 )
-    {
-        msg_Err( p_demux, "cannot get audio caps (%m)" );
+#ifdef HAVE_ALSA
+        int i_err;
+        snd_pcm_t *p_alsa_pcm;
+        char* psz_alsa_device_name = ResolveALSADeviceName( psz_device );
+
+        if( ( i_err = snd_pcm_open( &p_alsa_pcm, psz_alsa_device_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK ) ) < 0 )
+        {
+            msg_Err( p_demux, "cannot open device %s for ALSA audio (%s)", psz_alsa_device_name, snd_strerror( i_err ) );
+            free( psz_alsa_device_name );
+            goto open_failed;
+        }
+
+        snd_pcm_close( p_alsa_pcm );
+        free( psz_alsa_device_name );
+#else
+        msg_Err( p_demux, "ALSA support not available" );
         goto open_failed;
+#endif
+    } 
+    else
+    {
+        /* OSS */
+
+        if( ( i_fd = open( psz_device, O_RDONLY | O_NONBLOCK ) ) < 0 )
+        {
+            msg_Err( p_demux, "cannot open device %s for OSS audio (%m)", psz_device );
+            goto open_failed;
+        }
+
+        /* this will fail if the device is video */
+        if( ioctl( i_fd, SNDCTL_DSP_GETCAPS, &i_caps ) < 0 )
+        {
+            msg_Err( p_demux, "cannot get audio caps (%m)" );
+            goto open_failed;
+        }
+
+        if( i_fd >= 0 ) close( i_fd );
     }
 
-    if( i_fd >= 0 ) close( i_fd );
     return VLC_TRUE;
 
 open_failed: