yading@10: /* yading@10: * Linux audio play and grab interface yading@10: * Copyright (c) 2000, 2001 Fabrice Bellard yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include "config.h" yading@10: #include yading@10: #include yading@10: #include yading@10: #include yading@10: #include yading@10: #if HAVE_SOUNDCARD_H yading@10: #include yading@10: #else yading@10: #include yading@10: #endif yading@10: #include yading@10: #include yading@10: #include yading@10: yading@10: #include "libavutil/log.h" yading@10: #include "libavutil/opt.h" yading@10: #include "libavutil/time.h" yading@10: #include "libavcodec/avcodec.h" yading@10: #include "avdevice.h" yading@10: #include "libavformat/internal.h" yading@10: yading@10: #define AUDIO_BLOCK_SIZE 4096 yading@10: yading@10: typedef struct { yading@10: AVClass *class; yading@10: int fd; yading@10: int sample_rate; yading@10: int channels; yading@10: int frame_size; /* in bytes ! */ yading@10: enum AVCodecID codec_id; yading@10: unsigned int flip_left : 1; yading@10: uint8_t buffer[AUDIO_BLOCK_SIZE]; yading@10: int buffer_ptr; yading@10: } AudioData; yading@10: yading@10: static int audio_open(AVFormatContext *s1, int is_output, const char *audio_device) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: int audio_fd; yading@10: int tmp, err; yading@10: char *flip = getenv("AUDIO_FLIP_LEFT"); yading@10: yading@10: if (is_output) yading@10: audio_fd = open(audio_device, O_WRONLY); yading@10: else yading@10: audio_fd = open(audio_device, O_RDONLY); yading@10: if (audio_fd < 0) { yading@10: av_log(s1, AV_LOG_ERROR, "%s: %s\n", audio_device, strerror(errno)); yading@10: return AVERROR(EIO); yading@10: } yading@10: yading@10: if (flip && *flip == '1') { yading@10: s->flip_left = 1; yading@10: } yading@10: yading@10: /* non blocking mode */ yading@10: if (!is_output) { yading@10: if (fcntl(audio_fd, F_SETFL, O_NONBLOCK) < 0) { yading@10: av_log(s1, AV_LOG_WARNING, "%s: Could not enable non block mode (%s)\n", audio_device, strerror(errno)); yading@10: } yading@10: } yading@10: yading@10: s->frame_size = AUDIO_BLOCK_SIZE; yading@10: yading@10: /* select format : favour native format */ yading@10: err = ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &tmp); yading@10: yading@10: #if HAVE_BIGENDIAN yading@10: if (tmp & AFMT_S16_BE) { yading@10: tmp = AFMT_S16_BE; yading@10: } else if (tmp & AFMT_S16_LE) { yading@10: tmp = AFMT_S16_LE; yading@10: } else { yading@10: tmp = 0; yading@10: } yading@10: #else yading@10: if (tmp & AFMT_S16_LE) { yading@10: tmp = AFMT_S16_LE; yading@10: } else if (tmp & AFMT_S16_BE) { yading@10: tmp = AFMT_S16_BE; yading@10: } else { yading@10: tmp = 0; yading@10: } yading@10: #endif yading@10: yading@10: switch(tmp) { yading@10: case AFMT_S16_LE: yading@10: s->codec_id = AV_CODEC_ID_PCM_S16LE; yading@10: break; yading@10: case AFMT_S16_BE: yading@10: s->codec_id = AV_CODEC_ID_PCM_S16BE; yading@10: break; yading@10: default: yading@10: av_log(s1, AV_LOG_ERROR, "Soundcard does not support 16 bit sample format\n"); yading@10: close(audio_fd); yading@10: return AVERROR(EIO); yading@10: } yading@10: err=ioctl(audio_fd, SNDCTL_DSP_SETFMT, &tmp); yading@10: if (err < 0) { yading@10: av_log(s1, AV_LOG_ERROR, "SNDCTL_DSP_SETFMT: %s\n", strerror(errno)); yading@10: goto fail; yading@10: } yading@10: yading@10: tmp = (s->channels == 2); yading@10: err = ioctl(audio_fd, SNDCTL_DSP_STEREO, &tmp); yading@10: if (err < 0) { yading@10: av_log(s1, AV_LOG_ERROR, "SNDCTL_DSP_STEREO: %s\n", strerror(errno)); yading@10: goto fail; yading@10: } yading@10: yading@10: tmp = s->sample_rate; yading@10: err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &tmp); yading@10: if (err < 0) { yading@10: av_log(s1, AV_LOG_ERROR, "SNDCTL_DSP_SPEED: %s\n", strerror(errno)); yading@10: goto fail; yading@10: } yading@10: s->sample_rate = tmp; /* store real sample rate */ yading@10: s->fd = audio_fd; yading@10: yading@10: return 0; yading@10: fail: yading@10: close(audio_fd); yading@10: return AVERROR(EIO); yading@10: } yading@10: yading@10: static int audio_close(AudioData *s) yading@10: { yading@10: close(s->fd); yading@10: return 0; yading@10: } yading@10: yading@10: /* sound output support */ yading@10: static int audio_write_header(AVFormatContext *s1) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: AVStream *st; yading@10: int ret; yading@10: yading@10: st = s1->streams[0]; yading@10: s->sample_rate = st->codec->sample_rate; yading@10: s->channels = st->codec->channels; yading@10: ret = audio_open(s1, 1, s1->filename); yading@10: if (ret < 0) { yading@10: return AVERROR(EIO); yading@10: } else { yading@10: return 0; yading@10: } yading@10: } yading@10: yading@10: static int audio_write_packet(AVFormatContext *s1, AVPacket *pkt) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: int len, ret; yading@10: int size= pkt->size; yading@10: uint8_t *buf= pkt->data; yading@10: yading@10: while (size > 0) { yading@10: len = FFMIN(AUDIO_BLOCK_SIZE - s->buffer_ptr, size); yading@10: memcpy(s->buffer + s->buffer_ptr, buf, len); yading@10: s->buffer_ptr += len; yading@10: if (s->buffer_ptr >= AUDIO_BLOCK_SIZE) { yading@10: for(;;) { yading@10: ret = write(s->fd, s->buffer, AUDIO_BLOCK_SIZE); yading@10: if (ret > 0) yading@10: break; yading@10: if (ret < 0 && (errno != EAGAIN && errno != EINTR)) yading@10: return AVERROR(EIO); yading@10: } yading@10: s->buffer_ptr = 0; yading@10: } yading@10: buf += len; yading@10: size -= len; yading@10: } yading@10: return 0; yading@10: } yading@10: yading@10: static int audio_write_trailer(AVFormatContext *s1) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: yading@10: audio_close(s); yading@10: return 0; yading@10: } yading@10: yading@10: /* grab support */ yading@10: yading@10: static int audio_read_header(AVFormatContext *s1) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: AVStream *st; yading@10: int ret; yading@10: yading@10: st = avformat_new_stream(s1, NULL); yading@10: if (!st) { yading@10: return AVERROR(ENOMEM); yading@10: } yading@10: yading@10: ret = audio_open(s1, 0, s1->filename); yading@10: if (ret < 0) { yading@10: return AVERROR(EIO); yading@10: } yading@10: yading@10: /* take real parameters */ yading@10: st->codec->codec_type = AVMEDIA_TYPE_AUDIO; yading@10: st->codec->codec_id = s->codec_id; yading@10: st->codec->sample_rate = s->sample_rate; yading@10: st->codec->channels = s->channels; yading@10: yading@10: avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ yading@10: return 0; yading@10: } yading@10: yading@10: static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: int ret, bdelay; yading@10: int64_t cur_time; yading@10: struct audio_buf_info abufi; yading@10: yading@10: if ((ret=av_new_packet(pkt, s->frame_size)) < 0) yading@10: return ret; yading@10: yading@10: ret = read(s->fd, pkt->data, pkt->size); yading@10: if (ret <= 0){ yading@10: av_free_packet(pkt); yading@10: pkt->size = 0; yading@10: if (ret<0) return AVERROR(errno); yading@10: else return AVERROR_EOF; yading@10: } yading@10: pkt->size = ret; yading@10: yading@10: /* compute pts of the start of the packet */ yading@10: cur_time = av_gettime(); yading@10: bdelay = ret; yading@10: if (ioctl(s->fd, SNDCTL_DSP_GETISPACE, &abufi) == 0) { yading@10: bdelay += abufi.bytes; yading@10: } yading@10: /* subtract time represented by the number of bytes in the audio fifo */ yading@10: cur_time -= (bdelay * 1000000LL) / (s->sample_rate * s->channels); yading@10: yading@10: /* convert to wanted units */ yading@10: pkt->pts = cur_time; yading@10: yading@10: if (s->flip_left && s->channels == 2) { yading@10: int i; yading@10: short *p = (short *) pkt->data; yading@10: yading@10: for (i = 0; i < ret; i += 4) { yading@10: *p = ~*p; yading@10: p += 2; yading@10: } yading@10: } yading@10: return 0; yading@10: } yading@10: yading@10: static int audio_read_close(AVFormatContext *s1) yading@10: { yading@10: AudioData *s = s1->priv_data; yading@10: yading@10: audio_close(s); yading@10: return 0; yading@10: } yading@10: yading@10: #if CONFIG_OSS_INDEV yading@10: static const AVOption options[] = { yading@10: { "sample_rate", "", offsetof(AudioData, sample_rate), AV_OPT_TYPE_INT, {.i64 = 48000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, yading@10: { "channels", "", offsetof(AudioData, channels), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM }, yading@10: { NULL }, yading@10: }; yading@10: yading@10: static const AVClass oss_demuxer_class = { yading@10: .class_name = "OSS demuxer", yading@10: .item_name = av_default_item_name, yading@10: .option = options, yading@10: .version = LIBAVUTIL_VERSION_INT, yading@10: }; yading@10: yading@10: AVInputFormat ff_oss_demuxer = { yading@10: .name = "oss", yading@10: .long_name = NULL_IF_CONFIG_SMALL("OSS (Open Sound System) capture"), yading@10: .priv_data_size = sizeof(AudioData), yading@10: .read_header = audio_read_header, yading@10: .read_packet = audio_read_packet, yading@10: .read_close = audio_read_close, yading@10: .flags = AVFMT_NOFILE, yading@10: .priv_class = &oss_demuxer_class, yading@10: }; yading@10: #endif yading@10: yading@10: #if CONFIG_OSS_OUTDEV yading@10: AVOutputFormat ff_oss_muxer = { yading@10: .name = "oss", yading@10: .long_name = NULL_IF_CONFIG_SMALL("OSS (Open Sound System) playback"), yading@10: .priv_data_size = sizeof(AudioData), yading@10: /* XXX: we make the assumption that the soundcard accepts this format */ yading@10: /* XXX: find better solution with "preinit" method, needed also in yading@10: other formats */ yading@10: .audio_codec = AV_NE(AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S16LE), yading@10: .video_codec = AV_CODEC_ID_NONE, yading@10: .write_header = audio_write_header, yading@10: .write_packet = audio_write_packet, yading@10: .write_trailer = audio_write_trailer, yading@10: .flags = AVFMT_NOFILE, yading@10: }; yading@10: #endif