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.
 
+version next:
+
+- openal input device added
+
 
 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-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
@@ -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"
+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"
@@ -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
+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"
@@ -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 "openal enabled            ${openal-no}"
 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.
 
+@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.
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_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
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    (OPENAL, openal);
     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
-#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, \
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
+};