[FFmpeg-devel] [PATCH] Add support for sndio to libavdevice

Brad brad
Tue Aug 3 01:42:28 CEST 2010


sndio is a relatively new audio API utilized by OpenBSD.

Below is a patch to add sndio playback and record support
to FFmpeg.

I believe I have touched everything that needs updating
including documentation such as the recently added
indevs.texi and outdevs.texi, but if not please let
me know.

The sndio code was written by Jacob Meuser <jakemsr sdf lonestar org>

Please provide any feedback.


Index: configure
===================================================================
--- configure	(revision 24666)
+++ configure	(working copy)
@@ -1030,6 +1030,7 @@
     sdl
     sdl_video_size
     setmode
+    sndio_h
     socklen_t
     soundcard_h
     poll_h
@@ -1361,6 +1362,10 @@
 libdc1394_indev_deps="libdc1394"
 oss_indev_deps_any="soundcard_h sys_soundcard_h"
 oss_outdev_deps_any="soundcard_h sys_soundcard_h"
+sndio_indev_deps="sndio_h"
+sndio_indev_extralibs="-lsndio"
+sndio_outdev_deps="sndio_h"
+sndio_outdev_extralibs="-lsndio"
 v4l_indev_deps="linux_videodev_h"
 v4l2_indev_deps_any="linux_videodev2_h sys_videoio_h"
 vfwcap_indev_deps="capCreateCaptureWindow vfwcap_defines"
@@ -2772,11 +2777,14 @@
 
 check_header sys/soundcard.h
 check_header soundcard.h
+check_header sndio.h
 
 enabled_any alsa_indev alsa_outdev && check_lib2 alsa/asoundlib.h snd_pcm_htimestamp -lasound
 
 enabled jack_indev && check_lib2 jack/jack.h jack_client_open -ljack
 
+enabled_any sndio_indev sndio_outdev && check_lib2 sndio.h sio_open -lsndio
+
 enabled x11grab                         &&
 check_header X11/Xlib.h                 &&
 check_header X11/extensions/XShm.h      &&
Index: Changelog
===================================================================
--- Changelog	(revision 24666)
+++ Changelog	(working copy)
@@ -27,9 +27,9 @@
 - SubRip subtitle file muxer and demuxer
 - Chinese AVS encoding via libxavs
 - ffprobe -show_packets option added
+- sndio support for playback and record
 
 
-
 version 0.6:
 
 - PB-frame decoding for H.263
Index: doc/indevs.texi
===================================================================
--- doc/indevs.texi	(revision 24666)
+++ doc/indevs.texi	(working copy)
@@ -133,6 +133,23 @@
 For more information about OSS see:
 @url{http://manuals.opensound.com/usersguide/dsp.html}
 
+ at section sndio
+
+sndio input device.
+
+To enable this input device during configuration you need libsndio
+installed on your system.
+
+The filename to provide to the input device is the device node
+representing the sndio input device, and is usually set to
+ at file{/dev/audio0/}.
+
+For example to grab from @file{/dev/audio0/} using @file{ffmpeg} use the
+command:
+ at example
+ffmpeg -f sndio -i /dev/audio0 /tmp/oss.wav
+ at end example
+
 @section video4linux and video4linux2
 
 Video4Linux and Video4Linux2 input video devices.
Index: doc/outdevs.texi
===================================================================
--- doc/outdevs.texi	(revision 24666)
+++ doc/outdevs.texi	(working copy)
@@ -30,4 +30,8 @@
 
 OSS (Open Sound System) output device.
 
+ at section sndio
+
+sndio audio output device.
+
 @c man end OUTPUT DEVICES
Index: libavdevice/alldevices.c
===================================================================
--- libavdevice/alldevices.c	(revision 24666)
+++ libavdevice/alldevices.c	(working copy)
@@ -44,6 +44,7 @@
     REGISTER_INDEV    (DV1394, dv1394);
     REGISTER_INDEV    (JACK, jack);
     REGISTER_INOUTDEV (OSS, oss);
+    REGISTER_INOUTDEV (SNDIO, sndio);
     REGISTER_INDEV    (V4L2, v4l2);
     REGISTER_INDEV    (V4L, v4l);
     REGISTER_INDEV    (VFWCAP, vfwcap);
Index: libavdevice/sndio.c
===================================================================
--- libavdevice/sndio.c	(revision 0)
+++ libavdevice/sndio.c	(revision 0)
@@ -0,0 +1,299 @@
+/*
+ * sndio play and grab interface
+ * Copyright (c) 2010 Jacob Meuser
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sndio.h>
+
+#include "libavutil/log.h"
+#include "libavcodec/avcodec.h"
+#include "libavformat/avformat.h"
+
+
+typedef struct {
+    struct sio_hdl *hdl;
+    int sample_rate;
+    int channels;
+    int bps;
+    enum CodecID codec_id;
+    int buffer_size;
+    uint8_t *buffer;
+    int buffer_ptr;
+    long long hwpos, softpos;
+} AudioData;
+
+static void movecb(void *addr, int delta)
+{
+    AudioData *s = addr;
+
+    s->hwpos += delta * s->channels * s->bps;
+}
+
+static int audio_open(AVFormatContext *s1, int is_output, const char *audio_device)
+{
+    AudioData *s = s1->priv_data;
+    struct sio_hdl *hdl = NULL;
+    struct sio_par par;
+
+    if (is_output)
+        hdl = sio_open(audio_device, SIO_PLAY, 0);
+    else
+        hdl = sio_open(audio_device, SIO_REC, 0);
+    if (hdl == NULL) {
+        av_log(s1, AV_LOG_ERROR, "could not open sndio device\n");
+        return AVERROR(EIO);
+    }
+
+    sio_initpar(&par);
+    par.bits = 16;
+    par.sig = 1;
+    par.le = SIO_LE_NATIVE;
+    if (is_output)
+        par.pchan = s->channels;
+    else
+        par.rchan = s->channels;
+    par.rate = s->sample_rate;
+
+    if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) {
+        av_log(s1, AV_LOG_ERROR, "error setting sndio parameters\n");
+        goto fail;
+    }
+
+    if (par.bits != 16 || par.sig != 1 || par.le != SIO_LE_NATIVE ||
+      (is_output && (par.pchan != s->channels)) ||
+      (!is_output && (par.rchan != s->channels)) ||
+      (par.rate != s->sample_rate)) {
+        av_log(s1, AV_LOG_ERROR, "could not set appropriate sndio parameters\n");
+        goto fail;
+    }
+
+    s->buffer_size = par.round * par.bps *
+      (is_output ? par.pchan : par.rchan);
+
+    s->buffer = av_malloc(s->buffer_size);
+    if (s->buffer == NULL) {
+        av_log(s1, AV_LOG_ERROR, "could not allocate buffer\n");
+        goto fail;
+    }
+
+    if (par.le)
+        s->codec_id = CODEC_ID_PCM_S16LE;
+    else
+        s->codec_id = CODEC_ID_PCM_S16BE;
+
+    if (is_output)
+        s->channels = par.pchan;
+    else
+        s->channels = par.rchan;
+
+    s->sample_rate = par.rate;
+    s->bps = par.bps;
+
+    sio_onmove(hdl, movecb, s);
+
+    if (!sio_start(hdl)) {
+        av_log(s1, AV_LOG_ERROR, "could not start sndio\n");
+        goto fail;
+    }
+
+    s->hdl = hdl;
+
+    return 0;
+
+fail:
+    if (s->buffer)
+        av_free(s->buffer);
+    if (hdl)
+        sio_close(hdl);
+    return AVERROR(EIO);
+}
+
+static int audio_close(AudioData *s)
+{
+    if (s->buffer)
+        av_free(s->buffer);
+    if (s->hdl)
+        sio_close(s->hdl);
+    return 0;
+}
+
+/* sound output support */
+static int audio_write_header(AVFormatContext *s1)
+{
+    AudioData *s = s1->priv_data;
+    AVStream *st;
+    int ret;
+
+    st = s1->streams[0];
+    s->sample_rate = st->codec->sample_rate;
+    s->channels = st->codec->channels;
+    ret = audio_open(s1, 1, s1->filename);
+    if (ret < 0) {
+        return AVERROR(EIO);
+    } else {
+        return 0;
+    }
+}
+
+static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+    AudioData *s = s1->priv_data;
+    int len, ret;
+    int size = pkt->size;
+    uint8_t *buf= pkt->data;
+
+    while (size > 0) {
+        len = s->buffer_size - s->buffer_ptr;
+        if (len > size)
+            len = size;
+        memcpy(s->buffer + s->buffer_ptr, buf, len);
+        buf += len;
+        size -= len;
+        s->buffer_ptr += len;
+        if (s->buffer_ptr >= s->buffer_size) {
+            ret = sio_write(s->hdl, s->buffer, s->buffer_size);
+            if (ret == 0 || sio_eof(s->hdl))
+                return AVERROR(EIO);
+            s->softpos += ret;
+            s->buffer_ptr = 0;
+        }
+    }
+    return 0;
+}
+
+static int audio_write_trailer(AVFormatContext *s1)
+{
+    AudioData *s = s1->priv_data;
+
+    audio_close(s);
+    return 0;
+}
+
+/* grab support */
+
+static int audio_read_header(AVFormatContext *s1, AVFormatParameters *ap)
+{
+    AudioData *s = s1->priv_data;
+    AVStream *st;
+    int ret;
+
+    if (ap->sample_rate <= 0 || ap->channels <= 0)
+        return -1;
+
+    st = av_new_stream(s1, 0);
+    if (!st) {
+        return AVERROR(ENOMEM);
+    }
+    s->sample_rate = ap->sample_rate;
+    s->channels = ap->channels;
+
+    ret = audio_open(s1, 0, s1->filename);
+    if (ret < 0) {
+        return AVERROR(EIO);
+    }
+
+    /* take real parameters */
+    st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+    st->codec->codec_id = s->codec_id;
+    st->codec->sample_rate = s->sample_rate;
+    st->codec->channels = s->channels;
+
+    av_set_pts_info(st, 64, 1, 1000000);  /* 64 bits pts in us */
+    return 0;
+}
+
+static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
+{
+    AudioData *s = s1->priv_data;
+    int ret, bdelay;
+    int64_t cur_time;
+
+    if ((ret=av_new_packet(pkt, s->buffer_size)) < 0)
+        return ret;
+
+    ret = sio_read(s->hdl, pkt->data, pkt->size);
+    if (ret == 0 || sio_eof(s->hdl)) {
+        av_free_packet(pkt);
+        pkt->size = 0;
+        return AVERROR_EOF;
+    }
+    pkt->size = ret;
+    s->softpos += ret;
+
+    /* compute pts of the start of the packet */
+    cur_time = av_gettime();
+
+    bdelay = ret + s->hwpos - s->softpos;
+
+    /* convert to wanted units */
+    pkt->pts = cur_time;
+
+    return 0;
+}
+
+static int audio_read_close(AVFormatContext *s1)
+{
+    AudioData *s = s1->priv_data;
+
+    audio_close(s);
+    return 0;
+}
+
+#if CONFIG_SNDIO_INDEV
+AVInputFormat sndio_demuxer = {
+    "sndio",
+    NULL_IF_CONFIG_SMALL("sndio audio capture"),
+    sizeof(AudioData),
+    NULL,
+    audio_read_header,
+    audio_read_packet,
+    audio_read_close,
+    .flags = AVFMT_NOFILE,
+};
+#endif
+
+#if CONFIG_SNDIO_OUTDEV
+AVOutputFormat sndio_muxer = {
+    "sndio",
+    NULL_IF_CONFIG_SMALL("sndio audio playback"),
+    "",
+    "",
+    sizeof(AudioData),
+    /* XXX: we make the assumption that the soundcard accepts this format */
+    /* XXX: find better solution with "preinit" method, needed also in
+       other formats */
+#if HAVE_BIGENDIAN
+    CODEC_ID_PCM_S16BE,
+#else
+    CODEC_ID_PCM_S16LE,
+#endif
+    CODEC_ID_NONE,
+    audio_write_header,
+    audio_write_packet,
+    audio_write_trailer,
+    .flags = AVFMT_NOFILE,
+};
+#endif
Index: libavdevice/avdevice.h
===================================================================
--- libavdevice/avdevice.h	(revision 24666)
+++ libavdevice/avdevice.h	(working copy)
@@ -22,7 +22,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBAVDEVICE_VERSION_MAJOR 52
-#define LIBAVDEVICE_VERSION_MINOR  2
+#define LIBAVDEVICE_VERSION_MINOR  3
 #define LIBAVDEVICE_VERSION_MICRO  0
 
 #define LIBAVDEVICE_VERSION_INT AV_VERSION_INT(LIBAVDEVICE_VERSION_MAJOR, \
Index: libavdevice/Makefile
===================================================================
--- libavdevice/Makefile	(revision 24666)
+++ libavdevice/Makefile	(working copy)
@@ -17,6 +17,8 @@
 OBJS-$(CONFIG_JACK_INDEV)                += jack_audio.o
 OBJS-$(CONFIG_OSS_INDEV)                 += oss_audio.o
 OBJS-$(CONFIG_OSS_OUTDEV)                += oss_audio.o
+OBJS-$(CONFIG_SNDIO_INDEV)               += sndio.o
+OBJS-$(CONFIG_SNDIO_OUTDEV)              += sndio.o
 OBJS-$(CONFIG_V4L2_INDEV)                += v4l2.o
 OBJS-$(CONFIG_V4L_INDEV)                 += v4l.o
 OBJS-$(CONFIG_VFWCAP_INDEV)              += vfwcap.o

-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.




More information about the ffmpeg-devel mailing list