[FFmpeg-devel] [PATCH] OpenAL 1.1 Capture Support

Stefano Sabatini stefano.sabatini-lala at poste.it
Sat Jun 11 13:51:27 CEST 2011


On date Friday 2011-06-10 22:41:43 -0400, Jonathan Baldwin encoded:
> Sorry for the delay, I was kind of busy.
[...]
> I tried to fix this, but my IDE apparently doesn't support having no
> space between function name and params, so I went through manually.
> 
> >
> > Please mention these options in the manual. See how I did it in the
> > sdl section of the output devices chapter.
> >
> 
> Revamped the documentation thanks to your suggestion.
> Now organized into subsections.
> Added a table listing the three most common generic AL implementations
> including links to their web sites.
> Added an options subsection.
> Cleaned up examples, now in their own section. Added an example where
> an invalid device name is used, and explained that it will fail.
> 
> It seems quite extensive compared to the documentation for other
> indevs, but perhaps that's a good thing.

> diff --git a/Changelog b/Changelog
> index a2018dd..d809be5 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -21,6 +21,7 @@ version <next>:
>  - split filter added
>  - select filter added
>  - sdl output device added
> +- openal input device added
>  
>  
>  version 0.7_beta1:
> diff --git a/configure b/configure
> index 62b7e2f..7a83cbe 100755
> --- a/configure
> +++ b/configure
> @@ -188,6 +188,7 @@ External library support:
>    --enable-libxavs         enable AVS encoding via xavs [no]
>    --enable-libxvid         enable Xvid encoding via xvidcore,
>                             native MPEG-4/Xvid encoder exists [no]
> +  --enable-openal          enable OpenAL 1.1 capture support [no]
>    --enable-mlib            enable Sun medialib [no]
>    --enable-zlib            enable zlib [autodetect]
>  
> @@ -959,6 +960,7 @@ CONFIG_LIST="
>      mpegaudiodsp
>      network
>      nonfree
> +    openal
>      pic
>      postproc
>      rdft
> @@ -1470,6 +1472,7 @@ dv1394_indev_deps="dv1394 dv_demuxer"
>  fbdev_indev_deps="linux_fb_h"
>  jack_indev_deps="jack_jack_h sem_timedwait"
>  libdc1394_indev_deps="libdc1394"
> +openal_indev_deps="openal"
>  oss_indev_deps_any="soundcard_h sys_soundcard_h"
>  oss_outdev_deps_any="soundcard_h sys_soundcard_h"
>  sdl_outdev_deps="sdl"
> @@ -2944,6 +2947,9 @@ enabled libx264    && require  libx264 x264.h x264_encoder_encode -lx264 &&
>                          die "ERROR: libx264 version must be >= 0.115."; }
>  enabled libxavs    && require  libxavs xavs.h xavs_encoder_encode -lxavs
>  enabled libxvid    && require  libxvid xvid.h xvid_global -lxvidcore
> +enabled openal     && require openal 'AL/al.h' alGetError ${OPENAL_LIBS-'-lopenal'} &&
> +                      { check_cpp_condition "AL/al.h" "defined(AL_VERSION_1_1)" ||
> +                        die "ERROR: openal version must be 1.1 or compatible"; }
>  enabled mlib       && require  mediaLib mlib_types.h mlib_VectorSub_S16_U8_Mod -lmlib
>  
>  SDL_CONFIG="${cross_prefix}sdl-config"
> @@ -3233,6 +3239,7 @@ echo "libvpx enabled            ${libvpx-no}"
>  echo "libx264 enabled           ${libx264-no}"
>  echo "libxavs enabled           ${libxavs-no}"
>  echo "libxvid enabled           ${libxvid-no}"
> +echo "openal enabled            ${openal-no}"
>  echo "zlib enabled              ${zlib-no}"
>  echo "bzlib enabled             ${bzlib-no}"
>  echo
> diff --git a/doc/indevs.texi b/doc/indevs.texi
> index 0487108..49c9db9 100644
> --- a/doc/indevs.texi
> +++ b/doc/indevs.texi
> @@ -137,6 +137,81 @@ For more information read:
>  
>  IIDC1394 input device, based on libdc1394 and libraw1394.
>  
> + at section openal
> +
> +The OpenAL input device provides audio capture on all systems with a
> +working OpenAL 1.1 implementation.
> +
> +To enable this input device during configuration, you will need the
> +OpenAL headers and libraries. These should be provided as part of 
> +your OpenAL implementation, or as an additional download (an SDK).
> +
> +Incomplete List of OpenAL Implementations:
> + at multitable @columnfractions .15 .20 .15 .50
> +
> +  @headitem Vendor @tab Platform @tab License @tab Links
> +  
> +  @item Creative 
> +    @tab Windows 
> +    @tab Proprietary
> +    @tab @url{http://openal.org,Home Page} 
> +    @url{http://connect.creativelabs.com/openal/Downloads/Forms/AllItems.aspx,Downloads}
> +  
> +  @item OpenAL Soft 
> +    @tab Windows, Linux, Solaris, BSD 
> +    @tab LGPL 
> +    @tab @url{http://kcat.strangesoft.net/openal.html,Home Page}
> +  
> +  @item Apple 
> +    @tab Mac OS X 
> +    @tab 
> +    @tab OpenAL is part of 
> +      @url{http://developer.apple.com/library/mac/#documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html,Core Audio},
> +        the official OS X Audio interface. The SDK is included with XCode. 
> +  
> +  @end multitable

Please verify that this is correctly rendered in the man page (the
last time I checked, multi-tables are not correctly displayed), in
this case the previous approach would be just fine.

> + at subsection Options
> +
> + at table @option
> +
> + at item channels
> +The number of channels in the captured audio. Only the 
> +values 1 (monaural) and 2 (stereo) are currently supported.
> +Defaults to stereo.
> +
> + at item sample_size
> +The sample size (in bits) of the captured audio. Only the
> +values 8 and 16 are currently supported. Defaults to 16-bit.
> +
> + at item sample_rate
>
> +The sample rate (in Hz) of the captured audio. Defaults to 44100Hz.
>
> +
> + at end table
> +
> + at subsection Examples
> +
> +Capture from the OpenAL device 'DR-BT101 via PulseAudio':
> + at example
> +$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' out.ogg
> + at end example
> +
> +Capture from the default device (note the empty string '' as filename):
> + at example
> +$ ffmpeg -f openal -i '' out.ogg
> + at end example
> +
> +Capture from two devices simultaneously, writing to to different files, within same ffmpeg:
> + at example
> +$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' out1.ogg -f openal -i 'ALSA Default' out2.ogg
> + at end example
> +Note: Not all OpenAL implementations support multiple simultaneous capture, try the latest OpenAL Soft if the above doesn't work
> +
> +Using an invalid OpenAL device name will fail and a list of available devices will be printed:
> + at example
> +$ ffmpeg -f openal -i 'Not A Real OpenAL Device Name'
> + at end example

Note: it would be useful to list the available supported devices,
e.g. with a list option which returns AVERROR_EXIT (no need to do it
in this patch though).

> +
>  @section oss
>  Open Sound System input device.
> diff --git a/libavdevice/Makefile b/libavdevice/Makefile
> index 60103a4..4d3c1ae 100644
> --- a/libavdevice/Makefile
> +++ b/libavdevice/Makefile
> @@ -19,6 +19,7 @@ OBJS-$(CONFIG_DSHOW_INDEV)               += dshow.o dshow_enummediatypes.o \
>  OBJS-$(CONFIG_DV1394_INDEV)              += dv1394.o
>  OBJS-$(CONFIG_FBDEV_INDEV)               += fbdev.o
>  OBJS-$(CONFIG_JACK_INDEV)                += jack_audio.o
> +OBJS-$(CONFIG_OPENAL_INDEV)              += openal-dec.o
>  OBJS-$(CONFIG_OSS_INDEV)                 += oss_audio.o
>  OBJS-$(CONFIG_OSS_OUTDEV)                += oss_audio.o
>  OBJS-$(CONFIG_SDL_OUTDEV)                += sdl.o
> diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c
> index 7846704..ef302d7 100644
> --- a/libavdevice/alldevices.c
> +++ b/libavdevice/alldevices.c
> @@ -44,6 +44,7 @@ void avdevice_register_all(void)
>      REGISTER_INDEV    (DV1394, dv1394);
>      REGISTER_INDEV    (FBDEV, fbdev);
>      REGISTER_INDEV    (JACK, jack);
> +    REGISTER_INDEV    (OPENAL, openal);
>      REGISTER_INOUTDEV (OSS, oss);
>      REGISTER_OUTDEV   (SDL, sdl);
>      REGISTER_INOUTDEV (SNDIO, sndio);
> diff --git a/libavdevice/avdevice.h b/libavdevice/avdevice.h
> index be56be4..7cb8f54 100644
> --- a/libavdevice/avdevice.h
> +++ b/libavdevice/avdevice.h
> @@ -23,7 +23,7 @@
>  #include "libavformat/avformat.h"
>  
>  #define LIBAVDEVICE_VERSION_MAJOR 53
> -#define LIBAVDEVICE_VERSION_MINOR  1
> +#define LIBAVDEVICE_VERSION_MINOR  2
>  #define LIBAVDEVICE_VERSION_MICRO  0
>  
>  #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
> diff --git a/libavdevice/openal-dec.c b/libavdevice/openal-dec.c
> new file mode 100644
> index 0000000..e771e22
> --- /dev/null
> +++ b/libavdevice/openal-dec.c
> @@ -0,0 +1,251 @@
> +/** @file openal-dec.c OpenAL 1.1 capture device for libavdevice **/
> +/*
> + * Copyright (c) 2011 Jonathan Baldwin
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a copy
> + * of this software and associated documentation files (the "Software"), to deal
> + * in the Software without restriction, including without limitation the rights
> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
> + * copies of the Software, and to permit persons to whom the Software is
> + * furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
> + * THE SOFTWARE.
> + */
> +
> +#include "libavformat/avformat.h"
> +#include "libavutil/opt.h"
> +#include <AL/al.h>
> +#include <AL/alc.h>
> +

> +#define SSIZE 2048

SAMPLES_SIZE?

> +
> +typedef struct {
> +    ALCdevice *device;
> +

> +    ALCchar buffer[32768];

just curious, from where this magic number comes from?

> +    ALCint buffer_capacity;
> +    ALCint frame_size;
> +
> +    int channels, sample_rate, sample_size;
> +    ALCenum sample_format;
> +} al_data;
> +
> +typedef struct  {
> +    ALCenum al_fmt;
> +    enum CodecID codec_id;
> +    int channels;

> +    int frame_size;

Nit: sample_step looks more explicative (number of bytes between two
consecutives samples of the same channel/component).

> +} al_format_info;
> +
> +#define LOWEST_AL_FORMAT FFMIN(FFMIN(AL_FORMAT_MONO8,AL_FORMAT_MONO16),FFMIN(AL_FORMAT_STEREO8,AL_FORMAT_STEREO16))
> +
> +/**
> + * Get information about an AL_FORMAT value.
> + * @param al_fmt the AL_FORMAT value to find information about.
> + * @return A pointer to a structure containing information about the AL_FORMAT value.
> + */
> +static inline al_format_info* get_al_format_info(ALCenum al_fmt)
> +{
> +    static al_format_info info_table[] = {
> +        [AL_FORMAT_MONO8-LOWEST_AL_FORMAT]    = {AL_FORMAT_MONO8, CODEC_ID_PCM_U8, 1, 1},
> +        [AL_FORMAT_MONO16-LOWEST_AL_FORMAT]   = {AL_FORMAT_MONO16, AV_NE (CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE), 1, 2},
> +        [AL_FORMAT_STEREO8-LOWEST_AL_FORMAT]  = {AL_FORMAT_STEREO8, CODEC_ID_PCM_U8, 2, 2},
> +        [AL_FORMAT_STEREO16-LOWEST_AL_FORMAT] = {AL_FORMAT_STEREO16, AV_NE (CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE), 2, 4},
> +    };
> +
> +    return &info_table[al_fmt-LOWEST_AL_FORMAT];
> +}
> +
> +/**
> + * Get the OpenAL error code, translated into an av/errno error code.
> + * @param device The ALC device to check for errors.
> + * @param error_msg_ret A pointer to a char* in which to return the error message, or NULL if desired.
> + * @return The error code, or 0 if there is no error.
> + */
> +static inline int al_get_error(ALCdevice *device, const char** error_msg_ret)
> +{
> +    ALCenum error = alcGetError (device);
> +    if (error_msg_ret)
> +        *error_msg_ret = (const char*) alcGetString (device, error);
> +    switch (error) {
> +    case ALC_NO_ERROR:
> +        return 0;
> +    case ALC_INVALID_DEVICE:
> +        return AVERROR (ENODEV);
> +        break;
> +    case ALC_INVALID_CONTEXT:
> +    case ALC_INVALID_ENUM:
> +    case ALC_INVALID_VALUE:
> +        return AVERROR (EINVAL);
> +        break;
> +    case ALC_OUT_OF_MEMORY:
> +        return AVERROR (ENOMEM);
> +        break;
> +    default:
> +        return AVERROR (EIO);
> +    }
> +}

OK.

> +
> +/**
> + * Print out a list of OpenAL capture devices on this system.
> + */
> +static inline void print_al_capture_devices()
> +{
> +    const char *devices;
> +
> +    if (alcIsExtensionPresent (NULL, "ALC_ENUMERATION_EXT") != AL_TRUE)
> +        return;
> +
> +    if (! (devices = alcGetString (NULL, ALC_CAPTURE_DEVICE_SPECIFIER)))
> +        return;
> +
> +    av_log (NULL, AV_LOG_INFO, "List of OpenAL capture devices on this system:\n");
> +
> +    for (;*devices != '\0'; devices += strlen (devices) +1)
> +        av_log (NULL, AV_LOG_INFO, "\t%s\n", devices);
> +}
> +
> +static int read_header(AVFormatContext* ctx, AVFormatParameters *ap)
> +{
> +    static const ALCenum sample_formats[2][2] = {
> +        {AL_FORMAT_MONO8,AL_FORMAT_STEREO8},
> +        {AL_FORMAT_MONO16,AL_FORMAT_STEREO16}
> +    };
> +    al_data *ad = ctx->priv_data;
> +    int error=0;
> +    const char *error_msg;
> +    AVStream *st = NULL;
> +    AVCodecContext *codec = NULL;
> +
> +    ad->sample_format = sample_formats[ad->sample_size/8-1][ad->channels-1];
> +
> +    /* Open device for capture */
> +    ad->device = alcCaptureOpenDevice (
> +                     ctx->filename[0] ? ctx->filename : 0,
> +                     ad->sample_rate,
> +                     ad->sample_format,
> +                     SSIZE
> +                 );
> +
> +    /* Did we fail? */
> +    if (error=al_get_error (ad->device, &error_msg)) goto fail;
> +
> +    /* Create stream */
> +    if (! (st=av_new_stream (ctx, 0))) {
> +        error=AVERROR (ENOMEM);
> +        goto fail;
> +    }
> +
> +    /* We work in microseconds */
> +    av_set_pts_info (st, 64, 1, 1000000);
> +
> +    /* Set codec parameters */
> +    codec = st->codec;
> +    codec->codec_type = AVMEDIA_TYPE_AUDIO;
> +    codec->sample_rate = ad->sample_rate;
> +    codec->channels = get_al_format_info (ad->sample_format)->channels;
> +    codec->codec_id = get_al_format_info (ad->sample_format)->codec_id;
> +
> +    /* These are needed to read the audio data */
> +    ad->frame_size = get_al_format_info (ad->sample_format)->frame_size;
> +    ad->buffer_capacity = sizeof (ad->buffer) /ad->frame_size;
> +
> +    /* Finally, start the capture process */
> +    alcCaptureStart (ad->device);
> +
> +    return 0;
> +
> +    /* Handle failure */
> +fail:
> +    if (ad->device)
> +        alcCaptureCloseDevice (ad->device);
> +
> +    if (error_msg)
> +        av_log (ctx, AV_LOG_ERROR, "Error: %s\n", error_msg);
> +
> +    /* An EINVAL error here is probably due to an invalid OpenAL capture device name */
> +    if (error==AVERROR (EINVAL)) {
> +        av_log (ctx, AV_LOG_ERROR, "Did you use a valid OpenAL capture device name?\n");
> +        print_al_capture_devices();
> +    }
> +
> +    return error;
> +}
> +
> +static int read_packet(AVFormatContext* ctx, AVPacket *pkt)
> +{
> +    al_data *ad = ctx->priv_data;
> +    int error=0;
> +    const char *error_msg;

> +    ALCint num_samples;

Nit+++: nb_samples -> more consistent with the rest of FFmpeg

> +
> +    /* Read samples from device... */
> +    alcGetIntegerv (ad->device, ALC_CAPTURE_SAMPLES, (ALCsizei) sizeof (ALCint), &num_samples);
> +    if (error=al_get_error (ad->device, &error_msg)) goto fail;
> +
> +    if (num_samples > ad->buffer_capacity)
> +        num_samples = ad->buffer_capacity;
> +    alcCaptureSamples (ad->device, ad->buffer, num_samples);
> +    if (error=al_get_error (ad->device, &error_msg)) goto fail;
> +
> +    /* ...and use them to fill a packet */
> +    av_init_packet (pkt);
> +    pkt->data = ad->buffer;
> +    pkt->size = num_samples*ad->frame_size;
> +    pkt->pts = av_gettime();

I believe here you need to put the data directly into the packet.

You may get nb_samples, create a new packet with av_new_packet(),
and finally fill it with alcCaptureSamples().

[...]

Rest looks fine, nice work!
-- 
FFmpeg = Furious & Fiendish Monstrous Picky Entertaining Genius


More information about the ffmpeg-devel mailing list