lavdev: add openal input device
authorJonathan Baldwin <jbaldwin9182@gmail.com>
Sat, 25 Jun 2011 06:06:00 +0000 (08:06 +0200)
committerStefano Sabatini <stefano.sabatini-lala@poste.it>
Mon, 27 Jun 2011 13:26:14 +0000 (15:26 +0200)
Changelog
configure
doc/indevs.texi
libavdevice/Makefile
libavdevice/alldevices.c
libavdevice/avdevice.h
libavdevice/openal-dec.c [new file with mode: 0644]

index 4c2f2e3..70a912f 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -1,6 +1,10 @@
 Entries are sorted chronologically from oldest to youngest within each release,
 releases are sorted from youngest to oldest.
 
 Entries are sorted chronologically from oldest to youngest within each release,
 releases are sorted from youngest to oldest.
 
+version next:
+
+- openal input device added
+
 
 version 0.8:
 
 
 version 0.8:
 
index fb197aa..afa25d9 100755 (executable)
--- 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-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]
 
   --enable-mlib            enable Sun medialib [no]
   --enable-zlib            enable zlib [autodetect]
 
@@ -959,6 +960,7 @@ CONFIG_LIST="
     mpegaudiodsp
     network
     nonfree
     mpegaudiodsp
     network
     nonfree
+    openal
     pic
     postproc
     rdft
     pic
     postproc
     rdft
@@ -1471,6 +1473,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"
 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"
 oss_indev_deps_any="soundcard_h sys_soundcard_h"
 oss_outdev_deps_any="soundcard_h sys_soundcard_h"
 sdl_outdev_deps="sdl"
@@ -2948,6 +2951,11 @@ 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
                         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     && { { for al_libs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do
+                        check_lib 'AL/al.h' alGetError "${al_libs}" && break; done } ||
+                        die "ERROR: openal not found"; } &&
+                      { 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"
 enabled mlib       && require  mediaLib mlib_types.h mlib_VectorSub_S16_U8_Mod -lmlib
 
 SDL_CONFIG="${cross_prefix}sdl-config"
@@ -3247,6 +3255,7 @@ echo "libvpx enabled            ${libvpx-no}"
 echo "libx264 enabled           ${libx264-no}"
 echo "libxavs enabled           ${libxavs-no}"
 echo "libxvid enabled           ${libxvid-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
 echo "zlib enabled              ${zlib-no}"
 echo "bzlib enabled             ${bzlib-no}"
 echo
index 0487108..6d837ed 100644 (file)
@@ -137,6 +137,95 @@ For more information read:
 
 IIDC1394 input device, based on libdc1394 and libraw1394.
 
 
 IIDC1394 input device, based on libdc1394 and libraw1394.
 
+@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 need OpenAL
+headers and libraries installed on your system, and need to configure
+FFmpeg with @code{--enable-openal}.
+
+OpenAL headers and libraries should be provided as part of your OpenAL
+implementation, or as an additional download (an SDK). Depending on your
+installation you may need to specify additional flags via the
+@code{--extra-cflags} and @code{--extra-ldflags} for allowing the build
+system to locate the OpenAL headers and libraries.
+
+An incomplete list of OpenAL implementations follows:
+
+@table @strong
+@item Creative
+The official Windows implementation, providing hardware acceleration
+with supported devices and software fallback.
+See @url{http://openal.org/}.
+@item OpenAL Soft
+Portable, open source (LGPL) software implementation. Includes
+backends for the most common sound APIs on the Windows, Linux,
+Solaris, and BSD operating systems.
+See @url{http://kcat.strangesoft.net/openal.html}.
+@item Apple
+OpenAL is part of Core Audio, the official Mac OS X Audio interface.
+See @url{http://developer.apple.com/technologies/mac/audio-and-video.html}
+@end table
+
+This device allows to capture from an audio input device handled
+through OpenAL.
+
+You need to specify the name of the device to capture in the provided
+filename. If the empty string is provided, the device will
+automatically select the default device. You can get the list of the
+supported devices by using the option @var{list_devices}.
+
+@subsection Options
+
+@table @option
+
+@item channels
+Set the number of channels in the captured audio. Only the values
+@option{1} (monaural) and @option{2} (stereo) are currently supported.
+Defaults to @option{2}.
+
+@item sample_size
+Set the sample size (in bits) of the captured audio. Only the values
+@option{8} and @option{16} are currently supported. Defaults to
+@option{16}.
+
+@item sample_rate
+Set the sample rate (in Hz) of the captured audio.
+Defaults to @option{44.1k}.
+
+@item list_devices
+If set to @option{true}, print a list of devices and exit.
+Defaults to @option{false}.
+
+@end table
+
+@subsection Examples
+
+Print the list of OpenAL supported devices and exit:
+@example
+$ ffmpeg -list_devices true -f openal -i dummy out.ogg
+@end example
+
+Capture from the OpenAL device @file{DR-BT101 via PulseAudio}:
+@example
+$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' out.ogg
+@end example
+
+Capture from the default device (note the empty string '' as filename):
+@example
+$ ffmpeg -f openal -i '' out.ogg
+@end example
+
+Capture from two devices simultaneously, writing to two different files,
+within the same @file{ffmpeg} command:
+@example
+$ ffmpeg -f openal -i 'DR-BT101 via PulseAudio' out1.ogg -f openal -i 'ALSA Default' out2.ogg
+@end example
+Note: not all OpenAL implementations support multiple simultaneous capture -
+try the latest OpenAL Soft if the above does not work.
+
 @section oss
 
 Open Sound System input device.
 @section oss
 
 Open Sound System input device.
index 60103a4..4d3c1ae 100644 (file)
@@ -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_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
 OBJS-$(CONFIG_OSS_INDEV)                 += oss_audio.o
 OBJS-$(CONFIG_OSS_OUTDEV)                += oss_audio.o
 OBJS-$(CONFIG_SDL_OUTDEV)                += sdl.o
index 7846704..ef302d7 100644 (file)
@@ -44,6 +44,7 @@ void avdevice_register_all(void)
     REGISTER_INDEV    (DV1394, dv1394);
     REGISTER_INDEV    (FBDEV, fbdev);
     REGISTER_INDEV    (JACK, jack);
     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);
     REGISTER_INOUTDEV (OSS, oss);
     REGISTER_OUTDEV   (SDL, sdl);
     REGISTER_INOUTDEV (SNDIO, sndio);
index e49e5b7..7cb8f54 100644 (file)
@@ -23,8 +23,8 @@
 #include "libavformat/avformat.h"
 
 #define LIBAVDEVICE_VERSION_MAJOR 53
 #include "libavformat/avformat.h"
 
 #define LIBAVDEVICE_VERSION_MAJOR 53
-#define LIBAVDEVICE_VERSION_MINOR  1
-#define LIBAVDEVICE_VERSION_MICRO  1
+#define LIBAVDEVICE_VERSION_MINOR  2
+#define LIBAVDEVICE_VERSION_MICRO  0
 
 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
                                                LIBAVDEVICE_VERSION_MINOR, \
 
 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
                                                LIBAVDEVICE_VERSION_MINOR, \
diff --git a/libavdevice/openal-dec.c b/libavdevice/openal-dec.c
new file mode 100644 (file)
index 0000000..f01dff7
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2011 Jonathan Baldwin
+ *
+ * This file is part of FFmpeg.
+ *
+ * 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.
+ */
+
+/**
+ * @file
+ * OpenAL 1.1 capture device for libavdevice
+ **/
+
+#include <AL/al.h>
+#include <AL/alc.h>
+
+#include "libavutil/opt.h"
+#include "avdevice.h"
+
+typedef struct {
+    AVClass *class;
+    /** OpenAL capture device context. **/
+    ALCdevice *device;
+    /** The number of channels in the captured audio. **/
+    int channels;
+    /** The sample rate (in Hz) of the captured audio. **/
+    int sample_rate;
+    /** The sample size (in bits) of the captured audio. **/
+    int sample_size;
+    /** The OpenAL sample format of the captured audio. **/
+    ALCenum sample_format;
+    /** The number of bytes between two consecutive samples of the same channel/component. **/
+    ALCint sample_step;
+    /** If true, print a list of capture devices on this system and exit. **/
+    int list_devices;
+} al_data;
+
+typedef struct {
+    ALCenum al_fmt;
+    enum CodecID codec_id;
+    int channels;
+} 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},
+        [AL_FORMAT_MONO16-LOWEST_AL_FORMAT]   = {AL_FORMAT_MONO16, AV_NE (CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE), 1},
+        [AL_FORMAT_STEREO8-LOWEST_AL_FORMAT]  = {AL_FORMAT_STEREO8, CODEC_ID_PCM_U8, 2},
+        [AL_FORMAT_STEREO16-LOWEST_AL_FORMAT] = {AL_FORMAT_STEREO16, AV_NE (CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE), 2},
+    };
+
+    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);
+    }
+}
+
+/**
+ * Print out a list of OpenAL capture devices on this system.
+ */
+static inline void print_al_capture_devices(void *log_ctx)
+{
+    const char *devices;
+
+    if (!(devices = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)))
+        return;
+
+    av_log(log_ctx, AV_LOG_INFO, "List of OpenAL capture devices on this system:\n");
+
+    for (; *devices != '\0'; devices += strlen(devices) + 1)
+        av_log(log_ctx, AV_LOG_INFO, "  %s\n", devices);
+}
+
+static int read_header(AVFormatContext *ctx, AVFormatParameters *ap)
+{
+    al_data *ad = ctx->priv_data;
+    static const ALCenum sample_formats[2][2] = {
+        { AL_FORMAT_MONO8,  AL_FORMAT_STEREO8  },
+        { AL_FORMAT_MONO16, AL_FORMAT_STEREO16 }
+    };
+    int error = 0;
+    const char *error_msg;
+    AVStream *st = NULL;
+    AVCodecContext *codec = NULL;
+
+    if (ad->list_devices) {
+        print_al_capture_devices(ctx);
+        return AVERROR_EXIT;
+    }
+
+    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 : NULL,
+                             ad->sample_rate,
+                             ad->sample_format,
+                             ad->sample_rate); /* Maximum 1 second of sample data to be read at once */
+
+    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;
+
+    /* This is needed to read the audio data */
+    ad->sample_step = (av_get_bits_per_sample(get_al_format_info(ad->sample_format)->codec_id) *
+                       get_al_format_info(ad->sample_format)->channels) / 8;
+
+    /* Finally, start the capture process */
+    alcCaptureStart(ad->device);
+
+    return 0;
+
+fail:
+    /* Handle failure */
+    if (ad->device)
+        alcCaptureCloseDevice(ad->device);
+    if (error_msg)
+        av_log(ctx, AV_LOG_ERROR, "Cannot open device: %s\n", error_msg);
+    return error;
+}
+
+static int read_packet(AVFormatContext* ctx, AVPacket *pkt)
+{
+    al_data *ad = ctx->priv_data;
+    int error=0;
+    const char *error_msg;
+    ALCint nb_samples;
+
+    /* Get number of samples available */
+    alcGetIntegerv(ad->device, ALC_CAPTURE_SAMPLES, (ALCsizei) sizeof(ALCint), &nb_samples);
+    if (error = al_get_error(ad->device, &error_msg)) goto fail;
+
+    /* Create a packet of appropriate size */
+    av_new_packet(pkt, nb_samples*ad->sample_step);
+    pkt->pts = av_gettime();
+
+    /* Fill the packet with the available samples */
+    alcCaptureSamples(ad->device, pkt->data, nb_samples);
+    if (error = al_get_error(ad->device, &error_msg)) goto fail;
+
+    return pkt->size;
+fail:
+    /* Handle failure */
+    if (pkt->data)
+        av_destruct_packet(pkt);
+    if (error_msg)
+        av_log(ctx, AV_LOG_ERROR, "Error: %s\n", error_msg);
+    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;
+}
+
+#define OFFSET(x) offsetof(al_data, x)
+
+static const AVOption options[] = {
+    {"channels", "set number of channels",     OFFSET(channels),     FF_OPT_TYPE_INT, {.dbl=2},     1, 2,      AV_OPT_FLAG_DECODING_PARAM },
+    {"sample_rate", "set sample rate",         OFFSET(sample_rate),  FF_OPT_TYPE_INT, {.dbl=44100}, 1, 192000, AV_OPT_FLAG_DECODING_PARAM },
+    {"sample_size", "set sample size",         OFFSET(sample_size),  FF_OPT_TYPE_INT, {.dbl=16},    8, 16,     AV_OPT_FLAG_DECODING_PARAM },
+    {"list_devices", "list available devices", OFFSET(list_devices), FF_OPT_TYPE_INT, {.dbl=0},     0, 1,      AV_OPT_FLAG_DECODING_PARAM },
+    {"true",  "", 0, FF_OPT_TYPE_CONST, {.dbl=1}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" },
+    {"false", "", 0, FF_OPT_TYPE_CONST, {.dbl=0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM, "list_devices" },
+    {NULL},
+};
+
+static const AVClass class = {
+    .class_name = "openal",
+    .item_name = av_default_item_name,
+    .option = options,
+    .version = LIBAVUTIL_VERSION_INT
+};
+
+AVInputFormat ff_openal_demuxer = {
+    .name = "openal",
+    .long_name = NULL_IF_CONFIG_SMALL("OpenAL audio capture device"),
+    .priv_data_size = sizeof(al_data),
+    .read_probe = NULL,
+    .read_header = read_header,
+    .read_packet = read_packet,
+    .read_close = read_close,
+    .flags = AVFMT_NOFILE,
+    .priv_class = &class
+};