diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2011-10-22 01:56:57 +0200 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2011-10-22 01:56:57 +0200 |
commit | 5b602a140f3c071b67572ce5af2eb0469c5c8bed (patch) | |
tree | 2a47d6f024cb6960012d958745c753eae45466ed /libavdevice | |
parent | d000c5ae7e375ddf45215c550e9b2b815d3677af (diff) | |
parent | 796cd225dabd3b53340b7c6defc5e5e654de23d7 (diff) | |
download | ffmpeg-streaming-5b602a140f3c071b67572ce5af2eb0469c5c8bed.zip ffmpeg-streaming-5b602a140f3c071b67572ce5af2eb0469c5c8bed.tar.gz |
Merge remote-tracking branch 'luzero/pulse'
* luzero/pulse:
pulse: cosmetics
pulse: introduce pulseaudio input
Conflicts:
Changelog
configure
libavdevice/Makefile
libavdevice/alldevices.c
Merged-by: Michael Niedermayer <michaelni@gmx.at>
Diffstat (limited to 'libavdevice')
-rw-r--r-- | libavdevice/Makefile | 1 | ||||
-rw-r--r-- | libavdevice/alldevices.c | 1 | ||||
-rw-r--r-- | libavdevice/pulse.c | 191 |
3 files changed, 193 insertions, 0 deletions
diff --git a/libavdevice/Makefile b/libavdevice/Makefile index 97dd380..a640a04 100644 --- a/libavdevice/Makefile +++ b/libavdevice/Makefile @@ -24,6 +24,7 @@ OBJS-$(CONFIG_LAVFI_INDEV) += lavfi.o OBJS-$(CONFIG_OPENAL_INDEV) += openal-dec.o OBJS-$(CONFIG_OSS_INDEV) += oss_audio.o OBJS-$(CONFIG_OSS_OUTDEV) += oss_audio.o +OBJS-$(CONFIG_PULSE_INDEV) += pulse.o OBJS-$(CONFIG_SDL_OUTDEV) += sdl.o OBJS-$(CONFIG_SNDIO_INDEV) += sndio_common.o sndio_dec.o OBJS-$(CONFIG_SNDIO_OUTDEV) += sndio_common.o sndio_enc.o diff --git a/libavdevice/alldevices.c b/libavdevice/alldevices.c index ce2ea02..ace7c17 100644 --- a/libavdevice/alldevices.c +++ b/libavdevice/alldevices.c @@ -47,6 +47,7 @@ void avdevice_register_all(void) REGISTER_INDEV (LAVFI, lavfi); REGISTER_INDEV (OPENAL, openal); REGISTER_INOUTDEV (OSS, oss); + REGISTER_INDEV (PULSE, pulse); REGISTER_OUTDEV (SDL, sdl); REGISTER_INOUTDEV (SNDIO, sndio); REGISTER_INDEV (V4L2, v4l2); diff --git a/libavdevice/pulse.c b/libavdevice/pulse.c new file mode 100644 index 0000000..32d3948 --- /dev/null +++ b/libavdevice/pulse.c @@ -0,0 +1,191 @@ +/* + * Pulseaudio input + * Copyright (c) 2011 Luca Barbato <lu_zero@gentoo.org> + * + * This file is part of Libav. + * + * Libav 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. + * + * Libav 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 Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * Pulseaudio input + * @author Luca Barbato <lu_zero@gentoo.org> + * + * This avdevice decoder allows to capture audio from a Pulseaudio device using + * the simple api. + */ + +#include <pulse/simple.h> +#include <pulse/rtclock.h> +#include <pulse/error.h> + +#include "libavformat/avformat.h" +#include "libavutil/opt.h" + +#define DEFAULT_CODEC_ID AV_NE(CODEC_ID_PCM_S16BE, CODEC_ID_PCM_S16LE) + +typedef struct PulseData { + AVClass *class; + char *server; + char *name; + char *dev; + char *stream_name; + int sample_rate; + int channels; + int frame_size; + pa_simple *s; + int64_t pts; +} PulseData; + +static pa_sample_format_t codec_id_to_pulse_format(int codec_id) { + switch (codec_id) { + case CODEC_ID_PCM_U8: return PA_SAMPLE_U8; + case CODEC_ID_PCM_ALAW: return PA_SAMPLE_ALAW; + case CODEC_ID_PCM_MULAW: return PA_SAMPLE_ULAW; + case CODEC_ID_PCM_S16LE: return PA_SAMPLE_S16LE; + case CODEC_ID_PCM_S16BE: return PA_SAMPLE_S16BE; + case CODEC_ID_PCM_F32LE: return PA_SAMPLE_FLOAT32LE; + case CODEC_ID_PCM_F32BE: return PA_SAMPLE_FLOAT32BE; + case CODEC_ID_PCM_S32LE: return PA_SAMPLE_S32LE; + case CODEC_ID_PCM_S32BE: return PA_SAMPLE_S32BE; + case CODEC_ID_PCM_S24LE: return PA_SAMPLE_S24LE; + case CODEC_ID_PCM_S24BE: return PA_SAMPLE_S24BE; + default: return PA_SAMPLE_INVALID; + } +} + +static av_cold int pulse_read_header(AVFormatContext *s, + AVFormatParameters *ap) +{ + PulseData *pd = s->priv_data; + AVStream *st; + int ret; + enum CodecID codec_id = + s->audio_codec_id == CODEC_ID_NONE ? DEFAULT_CODEC_ID : s->audio_codec_id; + const pa_sample_spec ss = { codec_id_to_pulse_format(codec_id), + pd->sample_rate, + pd->channels }; + + pa_buffer_attr attr = { -1 }; + + st = avformat_new_stream(s, NULL); + + if (!st) { + av_log(s, AV_LOG_ERROR, "Cannot add stream\n"); + return AVERROR(ENOMEM); + } + + attr.fragsize = pd->frame_size * 4; + + pd->s = pa_simple_new(pd->server, pd->name, + PA_STREAM_RECORD, + pd->dev, pd->stream_name, &ss, + NULL, &attr, &ret); + if (!pd->s) { + av_log(s, AV_LOG_ERROR, "pa_simple_new failed: %s\n", + pa_strerror(ret)); + return AVERROR(EIO); + } + /* take real parameters */ + st->codec->codec_type = AVMEDIA_TYPE_AUDIO; + st->codec->codec_id = codec_id; + st->codec->sample_rate = pd->sample_rate; + st->codec->channels = pd->channels; + av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ + + return 0; +} + +static int pulse_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + PulseData *pd = s->priv_data; + int res; + pa_usec_t latency, cur; + uint64_t frame_duration = + (pd->frame_size*1000000LL)/(pd->sample_rate * pd->channels); + + if (av_new_packet(pkt, pd->frame_size) < 0) { + return AVERROR(ENOMEM); + } + + cur = pa_rtclock_now(); + + if ((pa_simple_read(pd->s, pkt->data, pkt->size, &res)) < 0) { + av_log(s, AV_LOG_ERROR, "pa_simple_read failed: %s\n", + pa_strerror(res)); + av_free_packet(pkt); + return AVERROR(EIO); + } + + if ((latency = pa_simple_get_latency(pd->s, &res)) == (pa_usec_t) -1) { + av_log(s, AV_LOG_ERROR, "pa_simple_get_latency() failed: %s\n", + pa_strerror(res)); + return AVERROR(EIO); + } + + if (!pd->pts) { + pd->pts -= latency; + } + + pd->pts += frame_duration; + + av_log(s, AV_LOG_DEBUG, "%"PRId64" time %"PRId64"," + " latency %"PRId64", %"PRId64"\n", + av_gettime(), cur, latency, pd->pts); + + pkt->pts = pd->pts; + + return 0; +} + +static av_cold int pulse_close(AVFormatContext *s) +{ + PulseData *pd = s->priv_data; + pa_simple_free(pd->s); + return 0; +} + +#define OFFSET(a) offsetof(PulseData, a) +#define D AV_OPT_FLAG_DECODING_PARAM + +static const AVOption options[] = { + { "server", "pulse server name", OFFSET(server), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D }, + { "name", "application name", OFFSET(name), AV_OPT_TYPE_STRING, {.str = "libav"}, 0, 0, D }, + { "dev", "device to use", OFFSET(dev), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, D }, + { "stream_name", "stream description", OFFSET(stream_name), AV_OPT_TYPE_STRING, {.str = "record"}, 0, 0, D }, + { "sample_rate", "", OFFSET(sample_rate), AV_OPT_TYPE_INT, {.dbl = 48000}, 1, INT_MAX, D }, + { "channels", "", OFFSET(channels), AV_OPT_TYPE_INT, {.dbl = 2}, 1, INT_MAX, D }, + { "frame_size", "", OFFSET(frame_size), AV_OPT_TYPE_INT, {.dbl = 1024}, 1, INT_MAX, D }, + { NULL }, +}; + +static const AVClass pulse_demuxer_class = { + .class_name = "Pulse demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVInputFormat ff_pulse_demuxer = { + .name = "pulse", + .long_name = NULL_IF_CONFIG_SMALL("Pulse audio input"), + .priv_data_size = sizeof(PulseData), + .read_header = pulse_read_header, + .read_packet = pulse_read_packet, + .read_close = pulse_close, + .flags = AVFMT_NOFILE, + .priv_class = &pulse_demuxer_class, +}; |