MythTV  master
audioinputalsa.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2007 Anand K. Mistry
3  * Copyright (C) 2007 Daniel Kristjansson
4  * Copyright (C) 2009 Alan Calvert
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02110-1301, USA.
20  */
21 
23 #include "audioinputalsa.h"
24 
25 #define LOC QString("AudioInALSA: ")
26 #define LOC_DEV QString("AudioInALSA(%1): ").arg(m_alsaDevice.constData())
27 
28 bool AudioInputALSA::Open(uint sample_bits, uint sample_rate, uint channels)
29 {
30  if (m_alsaDevice.isEmpty())
31  {
32  LOG(VB_GENERAL, LOG_ERR, LOC + QString("invalid alsa device name, %1")
33  .arg(m_alsaDevice.constData()));
34  return false;
35  }
36  (void)AlsaBad(snd_config_update_free_global(), "failed to update snd config");
37  m_audioSampleRate = sample_rate;
38  m_audioChannels = channels;
39  m_audioSampleBits = sample_bits;
40  if (AlsaBad(snd_pcm_open(&m_pcmHandle, m_alsaDevice.constData(),
41  SND_PCM_STREAM_CAPTURE, 0), // blocking mode
42  "pcm open failed"))
43  {
44  m_pcmHandle = nullptr;
45  return false;
46  }
47  if (!(PrepHwParams() && PrepSwParams()))
48  {
49  (void)snd_pcm_close(m_pcmHandle);
50  m_pcmHandle = nullptr;
51  return false;
52  }
53  LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "pcm open");
54  return true;
55 }
56 
58 {
59  if (m_pcmHandle != nullptr)
60  {
61  Stop();
62  (void)AlsaBad(snd_pcm_close(m_pcmHandle), "Close close failed");
63  }
64  m_pcmHandle = nullptr;
65 }
66 
68 {
69  bool stopped = false;
70  if (m_pcmHandle != nullptr &&
71  !AlsaBad(snd_pcm_drop(m_pcmHandle), "Stop drop failed"))
72  {
73  stopped = true;
74  LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "capture stopped");
75  }
76  return stopped;
77 }
78 
79 int AudioInputALSA::GetSamples(void* buf, uint nbytes)
80 {
81  if (!m_pcmHandle)
82  return 0;
83  int bytes_read = 0;
84  int pcm_state = snd_pcm_state(m_pcmHandle);
85  switch (pcm_state)
86  {
87  case SND_PCM_STATE_XRUN:
88  case SND_PCM_STATE_SUSPENDED:
89  {
90  bool recov = Stop() && Start();
91  LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "xrun recovery " +
92  (recov ? "good" : "not good"));
93  if (!recov)
94  break;
95  }
96  [[fallthrough]];
97  case SND_PCM_STATE_PREPARED:
98  if (AlsaBad(snd_pcm_start(m_pcmHandle), "pcm start failed"))
99  break;
100  [[fallthrough]];
101  case SND_PCM_STATE_RUNNING:
102  bytes_read = PcmRead(buf, nbytes);
103  break;
104  default:
105  LOG(VB_AUDIO, LOG_ERR, LOC_DEV +
106  QString("weird pcm state through GetSamples, %1")
107  .arg(pcm_state));
108  break;
109  }
110  return bytes_read;
111 }
112 
114 {
115  int bytes_avail = 0;
116  if (m_pcmHandle != nullptr)
117  {
118  snd_pcm_sframes_t frames_avail = 0;
119  int pcm_state = snd_pcm_state(m_pcmHandle);
120  switch (pcm_state)
121  {
122  case SND_PCM_STATE_PREPARED:
123  case SND_PCM_STATE_RUNNING:
124  frames_avail = snd_pcm_avail_update(m_pcmHandle);
125  if (!AlsaBad(frames_avail,
126  "GetNumReadyBytes, available update failed"))
127  bytes_avail = snd_pcm_frames_to_bytes(m_pcmHandle,
128  frames_avail);
129  }
130  }
131  return bytes_avail;
132 }
133 
135 {
136  snd_pcm_hw_params_t* hwparams = nullptr;
137  snd_pcm_hw_params_alloca(&hwparams);
138  if (AlsaBad(snd_pcm_hw_params_any(m_pcmHandle, hwparams),
139  "failed to init hw params"))
140  return false;
141  snd_pcm_access_t axs = SND_PCM_ACCESS_RW_INTERLEAVED; //always?
142  if (AlsaBad(snd_pcm_hw_params_set_access(m_pcmHandle, hwparams, axs),
143  "failed to set interleaved rw io"))
144  return false;
145  snd_pcm_format_t format =
146  (m_audioSampleBits > 8) ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8;
147  if (AlsaBad(snd_pcm_hw_params_set_format(m_pcmHandle, hwparams, format),
148  QString("failed to set sample format %1")
149  .arg(snd_pcm_format_description(format))))
150  return false;
151  if (VERBOSE_LEVEL_CHECK(VB_AUDIO, LOG_DEBUG))
152  {
153  uint min_chans = 0;
154  uint max_chans = 0;
155  if(AlsaBad(snd_pcm_hw_params_get_channels_min(hwparams, &min_chans),
156  QString("unable to get min channel count")))
157  min_chans = 0;
158  if(AlsaBad(snd_pcm_hw_params_get_channels_max(hwparams, &max_chans),
159  QString("unable to get max channel count")))
160  max_chans = 0;
161  LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV +
162  QString("min channels %1, max channels %2, myth requests %3")
163  .arg(min_chans).arg(max_chans).arg(m_audioChannels));
164  }
165  if (AlsaBad(snd_pcm_hw_params_set_channels(m_pcmHandle, hwparams,
166  m_audioChannels), QString("failed to set channels to %1")
167  .arg(m_audioChannels)))
168  {
169  return false;
170  }
171  if (AlsaBad(snd_pcm_hw_params_set_rate(m_pcmHandle, hwparams,
172  m_audioSampleRate, 0), QString("failed to set sample rate %1")
173  .arg(m_audioSampleRate)))
174  {
175  uint rate_num = 0;
176  uint rate_den = 0;
177  if (!AlsaBad(snd_pcm_hw_params_get_rate_numden(hwparams, &rate_num,
178  &rate_den), "snd_pcm_hw_params_get_rate_numden failed"))
179  {
180  if (m_audioSampleRate != (int)(rate_num / rate_den))
181  {
182  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
183  QString("device reports sample rate as %1")
184  .arg(rate_num / rate_den));
185  }
186  }
187  return false;
188  }
189  uint buffer_time = 64000; // 64 msec
190  uint period_time = buffer_time / 4;
191  if (AlsaBad(snd_pcm_hw_params_set_period_time_near(m_pcmHandle, hwparams, &period_time, nullptr),
192  "failed to set period time"))
193  return false;
194  if (AlsaBad(snd_pcm_hw_params_set_buffer_time_near(m_pcmHandle, hwparams, &buffer_time, nullptr),
195  "failed to set buffer time"))
196  return false;
197  if (AlsaBad(snd_pcm_hw_params_get_period_size(hwparams, &m_periodSize, nullptr),
198  "failed to get period size"))
199  return false;
200 
201  if (AlsaBad(snd_pcm_hw_params (m_pcmHandle, hwparams),
202  "failed to set hwparams"))
203  return false;
204 
205  m_mythBlockBytes = snd_pcm_frames_to_bytes(m_pcmHandle, m_periodSize);
206  LOG(VB_AUDIO, LOG_INFO, LOC_DEV +
207  QString("channels %1, sample rate %2, buffer_time %3 msec, period "
208  "size %4").arg(m_audioChannels)
209  .arg(m_audioSampleRate).arg(buffer_time / 1000.0, -1, 'f', 1)
210  .arg(m_periodSize));
211  LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV + QString("myth block size %1")
212  .arg(m_mythBlockBytes));
213  return true;
214 }
215 
217 {
218  snd_pcm_sw_params_t* swparams = nullptr;
219  snd_pcm_sw_params_alloca(&swparams);
220  snd_pcm_uframes_t boundary = 0;
221  if (AlsaBad(snd_pcm_sw_params_current(m_pcmHandle, swparams),
222  "failed to get swparams"))
223  return false;
224  if (AlsaBad(snd_pcm_sw_params_get_boundary(swparams, &boundary),
225  "failed to get boundary"))
226  return false;
227  // explicit start, not auto start
228  if (AlsaBad(snd_pcm_sw_params_set_start_threshold(m_pcmHandle, swparams,
229  boundary), "failed to set start threshold"))
230  return false;
231  if (AlsaBad(snd_pcm_sw_params_set_stop_threshold(m_pcmHandle, swparams,
232  boundary), "failed to set stop threshold"))
233  return false;
234  if (AlsaBad(snd_pcm_sw_params(m_pcmHandle, swparams),
235  "failed to set software parameters"))
236  return false;
237 
238  return true;
239 }
240 
241 int AudioInputALSA::PcmRead(void* buf, uint nbytes)
242 {
243  auto* bufptr = (unsigned char*)buf;
244  snd_pcm_uframes_t to_read = snd_pcm_bytes_to_frames(m_pcmHandle, nbytes);
245  snd_pcm_uframes_t nframes = to_read;
246  snd_pcm_sframes_t nread = 0;
247  snd_pcm_sframes_t avail = 0;
248  int retries = 0;
249  while (nframes > 0 && retries < 3)
250  {
251  avail = snd_pcm_avail_update(m_pcmHandle);
252  if (AlsaBad(avail, "available update failed"))
253  {
254  if (!Recovery(avail))
255  {
256  ++retries;
257  continue;
258  }
259  }
260  nread = snd_pcm_readi(m_pcmHandle, bufptr, nframes);
261  if (nread < 0)
262  {
263  switch (nread)
264  {
265  case -EAGAIN:
266  break;
267  case -EBADFD:
268  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
269  QString("in a state unfit to read (%1): %2")
270  .arg(nread).arg(snd_strerror(nread)));
271  break;
272  case -EINTR:
273  case -EPIPE:
274 #if ESTRPIPE != EPIPE
275  case -ESTRPIPE:
276 #endif
277  Recovery(nread);
278  break;
279  default:
280  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
281  QString("weird return from snd_pcm_readi: %1")
282  .arg(snd_strerror(nread)));
283  break;
284  }
285  }
286  else
287  {
288  nframes -= nread;
289  bufptr += snd_pcm_frames_to_bytes(m_pcmHandle, nread);
290  }
291  ++retries;
292  }
293  if (nframes > 0)
294  {
295  LOG(VB_AUDIO, LOG_ERR, LOC_DEV +
296  QString("short pcm read, %1 of %2 frames, retries %3")
297  .arg(to_read - nframes).arg(to_read).arg(retries));
298  }
299  return snd_pcm_frames_to_bytes(m_pcmHandle, to_read - nframes);
300 }
301 
303 {
304  if (err > 0)
305  err = -err;
306  bool isgood = false;
307  bool suspense = false;
308  switch (err)
309  {
310  case -EINTR:
311  isgood = true; // nothin' to see here
312  break;
313 #if ESTRPIPE != EPIPE
314  case -ESTRPIPE:
315  suspense = true;
316  [[fallthrough]];
317 #endif
318  case -EPIPE:
319  {
320  int ret = snd_pcm_prepare(m_pcmHandle);
321  if (ret < 0)
322  {
323  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
324  QString("failed to recover from %1. %2")
325  .arg(suspense ? "suspend" : "underrun",
326  snd_strerror(ret)));
327  return false;
328  }
329  isgood = true;
330  break;
331  }
332  default:
333  break;
334  }
335  return isgood;
336 }
337 
338 bool AudioInputALSA::AlsaBad(int op_result, const QString& errmsg)
339 { // (op_result < 0) => return true
340  bool bad = (op_result < 0);
341  if (bad)
342  LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
343  errmsg + ": " + snd_strerror(op_result));
344  return bad;
345 }
346 
347 /* vim: set expandtab tabstop=4 shiftwidth=4: */
audioinputalsa.h
AudioInputALSA::m_periodSize
snd_pcm_uframes_t m_periodSize
Definition: audioinputalsa.h:64
AudioInputALSA::PrepSwParams
bool PrepSwParams(void)
Definition: audioinputalsa.cpp:216
AudioInput::m_audioChannels
int m_audioChannels
Definition: audioinput.h:51
LOC
#define LOC
Definition: audioinputalsa.cpp:25
AudioInputALSA::Recovery
bool Recovery(int err)
Definition: audioinputalsa.cpp:302
VERBOSE_LEVEL_CHECK
static bool VERBOSE_LEVEL_CHECK(uint64_t mask, LogLevel_t level)
Definition: mythlogging.h:29
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
mythlogging.h
AudioInputALSA::m_mythBlockBytes
int m_mythBlockBytes
Definition: audioinputalsa.h:65
AudioInputALSA::m_pcmHandle
snd_pcm_t * m_pcmHandle
Definition: audioinputalsa.h:63
AudioInputALSA::PcmRead
int PcmRead(void *buf, uint nbytes)
Definition: audioinputalsa.cpp:241
AudioInputALSA::GetSamples
int GetSamples(void *buf, uint nbytes) override
Definition: audioinputalsa.cpp:79
AudioInputALSA::GetNumReadyBytes
int GetNumReadyBytes(void) override
Definition: audioinputalsa.cpp:113
AudioInputALSA::Stop
bool Stop(void) override
Definition: audioinputalsa.cpp:67
AudioInput::m_audioSampleRate
int m_audioSampleRate
Definition: audioinput.h:53
AudioInput::m_audioSampleBits
int m_audioSampleBits
Definition: audioinput.h:52
AudioInputALSA::PrepHwParams
bool PrepHwParams(void)
Definition: audioinputalsa.cpp:134
AudioInputALSA::AlsaBad
bool AlsaBad(int op_result, const QString &errmsg)
Definition: audioinputalsa.cpp:338
AudioInputALSA::Close
void Close(void) override
Definition: audioinputalsa.cpp:57
LOC_DEV
#define LOC_DEV
Definition: audioinputalsa.cpp:26
AudioInputALSA::Start
bool Start(void) override
Definition: audioinputalsa.h:46
AudioInputALSA::m_alsaDevice
QByteArray m_alsaDevice
Definition: audioinputalsa.h:62
AudioInputALSA::Open
bool Open(uint sample_bits, uint sample_rate, uint channels) override
Definition: audioinputalsa.cpp:28
uint
unsigned int uint
Definition: freesurround.h:24