1/* 2 * ALSA input and output 3 * Copyright (c) 2007 Luca Abeni ( lucabe72 email it ) 4 * Copyright (c) 2007 Benoit Fouet ( benoit fouet free fr ) 5 * 6 * This file is part of FFmpeg. 7 * 8 * FFmpeg is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * FFmpeg is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with FFmpeg; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23/** 24 * @file 25 * ALSA input and output: input 26 * @author Luca Abeni ( lucabe72 email it ) 27 * @author Benoit Fouet ( benoit fouet free fr ) 28 * @author Nicolas George ( nicolas george normalesup org ) 29 * 30 * This avdevice decoder allows to capture audio from an ALSA (Advanced 31 * Linux Sound Architecture) device. 32 * 33 * The filename parameter is the name of an ALSA PCM device capable of 34 * capture, for example "default" or "plughw:1"; see the ALSA documentation 35 * for naming conventions. The empty string is equivalent to "default". 36 * 37 * The capture period is set to the lower value available for the device, 38 * which gives a low latency suitable for real-time capture. 39 * 40 * The PTS are an Unix time in microsecond. 41 * 42 * Due to a bug in the ALSA library 43 * (https://bugtrack.alsa-project.org/alsa-bug/view.php?id=4308), this 44 * decoder does not work with certain ALSA plugins, especially the dsnoop 45 * plugin. 46 */ 47 48#include <alsa/asoundlib.h> 49#include "libavformat/internal.h" 50#include "libavutil/opt.h" 51#include "libavutil/mathematics.h" 52#include "libavutil/time.h" 53 54#include "avdevice.h" 55#include "alsa-audio.h" 56 57static av_cold int audio_read_header(AVFormatContext *s1) 58{ 59 AlsaData *s = s1->priv_data; 60 AVStream *st; 61 int ret; 62 enum AVCodecID codec_id; 63 64 st = avformat_new_stream(s1, NULL); 65 if (!st) { 66 av_log(s1, AV_LOG_ERROR, "Cannot add stream\n"); 67 68 return AVERROR(ENOMEM); 69 } 70 codec_id = s1->audio_codec_id; 71 72 ret = ff_alsa_open(s1, SND_PCM_STREAM_CAPTURE, &s->sample_rate, s->channels, 73 &codec_id); 74 if (ret < 0) { 75 return AVERROR(EIO); 76 } 77 78 /* take real parameters */ 79 st->codec->codec_type = AVMEDIA_TYPE_AUDIO; 80 st->codec->codec_id = codec_id; 81 st->codec->sample_rate = s->sample_rate; 82 st->codec->channels = s->channels; 83 avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ 84 /* microseconds instead of seconds, MHz instead of Hz */ 85 s->timefilter = ff_timefilter_new(1000000.0 / s->sample_rate, 86 s->period_size, 1.5E-6); 87 if (!s->timefilter) 88 goto fail; 89 90 return 0; 91 92fail: 93 snd_pcm_close(s->h); 94 return AVERROR(EIO); 95} 96 97static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) 98{ 99 AlsaData *s = s1->priv_data; 100 int res; 101 int64_t dts; 102 snd_pcm_sframes_t delay = 0; 103 104 if (av_new_packet(pkt, s->period_size * s->frame_size) < 0) { 105 return AVERROR(EIO); 106 } 107 108 while ((res = snd_pcm_readi(s->h, pkt->data, s->period_size)) < 0) { 109 if (res == -EAGAIN) { 110 av_free_packet(pkt); 111 112 return AVERROR(EAGAIN); 113 } 114 if (ff_alsa_xrun_recover(s1, res) < 0) { 115 av_log(s1, AV_LOG_ERROR, "ALSA read error: %s\n", 116 snd_strerror(res)); 117 av_free_packet(pkt); 118 119 return AVERROR(EIO); 120 } 121 ff_timefilter_reset(s->timefilter); 122 } 123 124 dts = av_gettime(); 125 snd_pcm_delay(s->h, &delay); 126 dts -= av_rescale(delay + res, 1000000, s->sample_rate); 127 pkt->pts = ff_timefilter_update(s->timefilter, dts, s->last_period); 128 s->last_period = res; 129 130 pkt->size = res * s->frame_size; 131 132 return 0; 133} 134 135static const AVOption options[] = { 136 { "sample_rate", "", offsetof(AlsaData, sample_rate), AV_OPT_TYPE_INT, {.i64 = 48000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, 137 { "channels", "", offsetof(AlsaData, channels), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, 138 { NULL }, 139}; 140 141static const AVClass alsa_demuxer_class = { 142 .class_name = "ALSA demuxer", 143 .item_name = av_default_item_name, 144 .option = options, 145 .version = LIBAVUTIL_VERSION_INT, 146 .category = AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT, 147}; 148 149AVInputFormat ff_alsa_demuxer = { 150 .name = "alsa", 151 .long_name = NULL_IF_CONFIG_SMALL("ALSA audio input"), 152 .priv_data_size = sizeof(AlsaData), 153 .read_header = audio_read_header, 154 .read_packet = audio_read_packet, 155 .read_close = ff_alsa_close, 156 .flags = AVFMT_NOFILE, 157 .priv_class = &alsa_demuxer_class, 158}; 159