[FFmpeg-devel] [PATCH] OpenAL 1.1 Capture Support
Stefano Sabatini
stefano.sabatini-lala at poste.it
Mon Jun 6 11:02:03 CEST 2011
On date Sunday 2011-06-05 17:05:28 -0400, Jonathan Baldwin encoded:
> > On Windows openal library mostly named as "OpenAL32.lib"
> The environment variable OPENAL_LIBS is now used for OpenAL
> library-related flags if it exists, otherwise -lopenal is used.
> Windows builders can set the environment variable appropriately.
[...]
> My pleasure. An updated patch is attached.
> 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..88458d1 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,8 @@ 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"
> +openal_indev_extralibs="${OPENAL_LIBS-'-lopenal'}"
is this required?...
> oss_indev_deps_any="soundcard_h sys_soundcard_h"
> oss_outdev_deps_any="soundcard_h sys_soundcard_h"
> sdl_outdev_deps="sdl"
> @@ -2944,6 +2948,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'} &&
^^^^^^^^^^^^^^^^^^^^^^^^^
...isn't this enough?
> + { 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 +3240,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..427c6da 100644
> --- a/doc/indevs.texi
> +++ b/doc/indevs.texi
> @@ -137,6 +137,48 @@ 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 are provided by the OpenAL Soft
> +implementation and as part of an SDK from Creative Labs.
> +
> +To capture from a specific sound device, provide the OpenAL name of
> +that device as the filename:
> + at example
> +$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' /tmp/out.ogg
> + at end example
> +Note that the device names used are different for each OpenAL
> +implementation. OpenAL Soft provides a tool called
> + at file{openal-info} which can be used to list the devices on
> +a system.
> +
> +Or, just pass a blank string as the filename to use the default
> +device:
> + at example
> +$ ffmpeg -f openal -i '' /tmp/out.ogg
> + at end example
> +
> +Some OpenAL implementations (such as OpenAL Soft) support capturing
> +from two devices simultaneously within a single process:
> + at example
> +$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' /tmp/out1.ogg -f openal -i 'ALSA Default' /tmp/out2.ogg
> + at end example
> +If this doesn't work (crashes, duplicate audio, or worse) it is
> +probably your OpenAL implementation; try using the latest version
> +of OpenAL Soft and see if that works.
OK, maybe we could prefer plain filenames like out.ogg, /tmp/x.ogg
looks very UNIX-ish, you may alienate some Windows user ;-).
> +
> +Links:
> + at itemize
> + at item
> +Creative Labs OpenAL Homepage @url{http://openal.org}
> + at item
> +OpenAL Soft @url{http://kcat.strangesoft.net/openal.html}
> + at end itemize
> +
> @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..1885c5c
> --- /dev/null
> +++ b/libavdevice/openal-dec.c
> @@ -0,0 +1,210 @@
> +/** @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
> +
> +typedef struct {
> + ALCdevice *device;
> +
> + ALCchar buffer[32768];
> + ALCint buffer_capacity;
> + ALCint sample_size;
> +
> + int channels, sample_rate;
> + ALCenum sample_format;
> +} al_data;
> +
> +typedef struct {
> + ALCenum al_fmt;
> + enum CodecID codec_id;
> + int channels;
> + int sample_size;
> +} al_format_info;
> +
> +//Preprocessor magic to find the lowest AL_FORMAT value
> +#define FMIN_(a,b) ((a<b) ? a : b)
This is FFMIN defined in libavutil.
> +#define FMIN_2(a,b) FMIN_(a,b)
Is this required?
> +#define FMIN_4(a,b,c,d) FMIN_( FMIN_2(a,b), FMIN_2(c,d) )
> +#define LOWEST_AL_FORMAT FMIN_4(AL_FORMAT_MONO8, AL_FORMAT_MONO16, AL_FORMAT_STEREO8, AL_FORMAT_STEREO16)
What about using FFMIN(FFMIN(a,b), FFMIN(c,d))?
> +
> +/**
> + * Get information about an AL_FORMAT value.
> + * @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},
> + };
You may put each entry on a separate line, and vertically align fields.
> +
> + return &info_table[al_fmt-LOWEST_AL_FORMAT];
> +}
> +
> +/**
> + * Get the OpenAL error code, translated into an av/errno error code.
> + * @return The error code, or 0 if there is no error.
> + */
> +static inline int al_get_error (ALCdevice *device) {
> + ALCenum error = alcGetError (device);
> + 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 (INT_MAX);
return a generic AVERROR(EIO) or AVERROR(EINVAL), AVERROR(INT_MAX)
semantics is undefined.
Also providing the error string was a nice feature.
> + }
> +}
> +
> +static int read_header (AVFormatContext* ctx, AVFormatParameters *ap) {
> + al_data *ad = ctx->priv_data;
> + int error=0;
> + AVStream *st = NULL;
> + AVCodecContext *codec = NULL;
> +
> + ad->sample_format = (ad->channels==1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
> +
> + /* 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)) goto fail1;
> +
> + /* Create stream */
> + if (! (st=av_new_stream (ctx, 0))) {
> + error=AVERROR (ENOMEM);
> + goto fail2;
> + }
> +
> + /* 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->sample_size = get_al_format_info (ad->sample_format)->sample_size;
> + ad->buffer_capacity = sizeof (ad->buffer) /ad->sample_size;
> +
> + /* Finally, start the capture process */
> + alcCaptureStart (ad->device);
> +
> + return 0;
> +
> + /* Handle faliure */
typo
> +fail2:
> + alcCaptureCloseDevice (ad->device);
> +fail1:
> + return error;
alcCaptureOpenDevice() return NULL in case of failure, so you can use
just a single "fail" label and do:
fail:
if (ad->device)
alcCaptureCloseDevice(ad->device);
return error;
Printing the error here as in the previous version is a good idea
(improves feedback).
> +fail1:
> + return error;
> +}
> +
> +static int read_packet (AVFormatContext* ctx, AVPacket *pkt) {
> + al_data *ad = ctx->priv_data;
> + int error=0;
> + ALCint num_samples;
> +
> + /* Read samples from device... */
> + alcGetIntegerv (ad->device, ALC_CAPTURE_SAMPLES, (ALCsizei) sizeof (ALCint), &num_samples);
> + if (error=al_get_error (ad->device)) 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)) goto fail;
> +
> + /* ...and use them to fill a packet */
> + av_init_packet (pkt);
> + pkt->data = ad->buffer;
> + pkt->size = num_samples*ad->sample_size;
> + pkt->pts = av_gettime();
> +
> + return pkt->size;
> +fail:
> + /* Handle failure */
> + return error;
> +}
> +
> +static int read_close (AVFormatContext* ctx) {
> + al_data *ad = ctx->priv_data;
> +
> + if (ad->device) {
> + alcCaptureStop (ad->device);
> + alcCaptureCloseDevice (ad->device);
> + }
> + return 0;
> +}
More style nits:
static int read_close(AVFormatContext* ctx)
{
al_data *ad = ctx->priv_data;
if (ad->device) {
alcCaptureStop(ad->device);
alcCaptureCloseDevice(ad->device);
}
return 0;
}
that is: 4 chars of indent, no space between fun_name and params, and
"{" on a starting line for functions. No need to address these nits if
you don't care, I can do it myself with a few emacs keystrokes before
committing.
> +
> +static const AVOption options[] = {
> + {"channels", "", offsetof (al_data, channels), FF_OPT_TYPE_INT, {.dbl=2}, 1, 2, AV_OPT_FLAG_DECODING_PARAM },
> + {"sample_rate", "", offsetof (al_data, sample_rate), FF_OPT_TYPE_INT, {.dbl=44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
> + {NULL},
Please mention these options in the manual. See how I did it in the
sdl section of the output devices chapter.
[...]
--
FFmpeg = Fiendish and Fanciful Monstrous Powered Enchanting Gadget
More information about the ffmpeg-devel
mailing list