You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

219 lines
5.7KB

  1. /*
  2. * sndio grab interface
  3. * Copyright (c) 2010 Jacob Meuser
  4. *
  5. * This file is part of Libav.
  6. *
  7. * Libav is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * Libav is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with Libav; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include <stdint.h>
  22. #include <sndio.h>
  23. #include "libavutil/internal.h"
  24. #include "libavutil/opt.h"
  25. #include "libavutil/time.h"
  26. #include "libavformat/avformat.h"
  27. #include "libavformat/internal.h"
  28. typedef struct SndioData {
  29. AVClass *class;
  30. struct sio_hdl *hdl;
  31. enum AVCodecID codec_id;
  32. int64_t hwpos;
  33. int64_t softpos;
  34. uint8_t *buffer;
  35. int bps;
  36. int buffer_size;
  37. int buffer_offset;
  38. int channels;
  39. int sample_rate;
  40. } SndioData;
  41. static inline void movecb(void *addr, int delta)
  42. {
  43. SndioData *s = addr;
  44. s->hwpos += delta * s->channels * s->bps;
  45. }
  46. static av_cold int sndio_open(AVFormatContext *s1, int is_output,
  47. const char *audio_device)
  48. {
  49. SndioData *s = s1->priv_data;
  50. struct sio_hdl *hdl;
  51. struct sio_par par;
  52. hdl = sio_open(audio_device, is_output ? SIO_PLAY : SIO_REC, 0);
  53. if (!hdl) {
  54. av_log(s1, AV_LOG_ERROR, "Could not open sndio device\n");
  55. return AVERROR(EIO);
  56. }
  57. sio_initpar(&par);
  58. par.bits = 16;
  59. par.sig = 1;
  60. par.le = SIO_LE_NATIVE;
  61. if (is_output)
  62. par.pchan = s->channels;
  63. else
  64. par.rchan = s->channels;
  65. par.rate = s->sample_rate;
  66. if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) {
  67. av_log(s1, AV_LOG_ERROR, "Impossible to set sndio parameters, "
  68. "channels: %d sample rate: %d\n", s->channels, s->sample_rate);
  69. goto fail;
  70. }
  71. if (par.bits != 16 || par.sig != 1 ||
  72. (is_output && (par.pchan != s->channels)) ||
  73. (!is_output && (par.rchan != s->channels)) ||
  74. (par.rate != s->sample_rate)) {
  75. av_log(s1, AV_LOG_ERROR, "Could not set appropriate sndio parameters, "
  76. "channels: %d sample rate: %d\n", s->channels, s->sample_rate);
  77. goto fail;
  78. }
  79. s->buffer_size = par.round * par.bps *
  80. (is_output ? par.pchan : par.rchan);
  81. if (is_output) {
  82. s->buffer = av_malloc(s->buffer_size);
  83. if (!s->buffer) {
  84. av_log(s1, AV_LOG_ERROR, "Could not allocate buffer\n");
  85. goto fail;
  86. }
  87. }
  88. s->codec_id = par.le ? AV_CODEC_ID_PCM_S16LE : AV_CODEC_ID_PCM_S16BE;
  89. s->channels = is_output ? par.pchan : par.rchan;
  90. s->sample_rate = par.rate;
  91. s->bps = par.bps;
  92. sio_onmove(hdl, movecb, s);
  93. if (!sio_start(hdl)) {
  94. av_log(s1, AV_LOG_ERROR, "Could not start sndio\n");
  95. goto fail;
  96. }
  97. s->hdl = hdl;
  98. return 0;
  99. fail:
  100. av_freep(&s->buffer);
  101. if (hdl)
  102. sio_close(hdl);
  103. return AVERROR(EIO);
  104. }
  105. static av_cold int audio_read_header(AVFormatContext *s1)
  106. {
  107. SndioData *s = s1->priv_data;
  108. AVStream *st;
  109. int ret;
  110. st = avformat_new_stream(s1, NULL);
  111. if (!st)
  112. return AVERROR(ENOMEM);
  113. ret = sndio_open(s1, 0, s1->filename);
  114. if (ret < 0)
  115. return ret;
  116. /* take real parameters */
  117. st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
  118. st->codecpar->codec_id = s->codec_id;
  119. st->codecpar->sample_rate = s->sample_rate;
  120. st->codecpar->channels = s->channels;
  121. avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
  122. return 0;
  123. }
  124. static int audio_read_packet(AVFormatContext *s1, AVPacket *pkt)
  125. {
  126. SndioData *s = s1->priv_data;
  127. int64_t bdelay, cur_time;
  128. int ret;
  129. if ((ret = av_new_packet(pkt, s->buffer_size)) < 0)
  130. return ret;
  131. ret = sio_read(s->hdl, pkt->data, pkt->size);
  132. if (ret == 0 || sio_eof(s->hdl)) {
  133. av_packet_unref(pkt);
  134. return AVERROR_EOF;
  135. }
  136. pkt->size = ret;
  137. s->softpos += ret;
  138. /* compute pts of the start of the packet */
  139. cur_time = av_gettime();
  140. bdelay = ret + s->hwpos - s->softpos;
  141. /* convert to pts */
  142. pkt->pts = cur_time - ((bdelay * 1000000) /
  143. (s->bps * s->channels * s->sample_rate));
  144. return 0;
  145. }
  146. static av_cold int audio_read_close(AVFormatContext *s1)
  147. {
  148. SndioData *s = s1->priv_data;
  149. av_freep(&s->buffer);
  150. if (s->hdl)
  151. sio_close(s->hdl);
  152. return 0;
  153. }
  154. static const AVOption options[] = {
  155. { "sample_rate", "", offsetof(SndioData, sample_rate), AV_OPT_TYPE_INT, {.i64 = 48000}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
  156. { "channels", "", offsetof(SndioData, channels), AV_OPT_TYPE_INT, {.i64 = 2}, 1, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
  157. { NULL },
  158. };
  159. static const AVClass sndio_demuxer_class = {
  160. .class_name = "sndio indev",
  161. .item_name = av_default_item_name,
  162. .option = options,
  163. .version = LIBAVUTIL_VERSION_INT,
  164. };
  165. AVInputFormat ff_sndio_demuxer = {
  166. .name = "sndio",
  167. .long_name = NULL_IF_CONFIG_SMALL("sndio audio capture"),
  168. .priv_data_size = sizeof(SndioData),
  169. .read_header = audio_read_header,
  170. .read_packet = audio_read_packet,
  171. .read_close = audio_read_close,
  172. .flags = AVFMT_NOFILE,
  173. .priv_class = &sndio_demuxer_class,
  174. };