00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "mythlogging.h"
00023 #include "audioinputalsa.h"
00024
00025 #define LOC QString("AudioInALSA: ")
00026 #define LOC_DEV QString("AudioInALSA(%1): ").arg(alsa_device.constData())
00027
00028 AudioInputALSA::AudioInputALSA(const QString &device):
00029 AudioInput(device),
00030 pcm_handle(NULL),
00031 period_size(0),
00032 myth_block_bytes(0)
00033 {
00034 alsa_device = device.right(device.size()-5).toAscii();
00035 }
00036
00037 bool AudioInputALSA::Open(uint sample_bits, uint sample_rate, uint channels)
00038 {
00039 if (alsa_device.isEmpty())
00040 {
00041 LOG(VB_GENERAL, LOG_ERR, LOC + QString("invalid alsa device name, %1")
00042 .arg(alsa_device.constData()));
00043 return false;
00044 }
00045 AlsaBad(snd_config_update_free_global(), "failed to update snd config");
00046 m_audio_sample_rate = sample_rate;
00047 m_audio_channels = channels;
00048 m_audio_sample_bits = sample_bits;
00049 if (AlsaBad(snd_pcm_open(&pcm_handle, alsa_device.constData(),
00050 SND_PCM_STREAM_CAPTURE, 0),
00051 "pcm open failed"))
00052 {
00053 pcm_handle = NULL;
00054 return false;
00055 }
00056 if (!(PrepHwParams() && PrepSwParams()))
00057 {
00058 snd_pcm_close(pcm_handle);
00059 pcm_handle = NULL;
00060 return false;
00061 }
00062 LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "pcm open");
00063 return true;
00064 }
00065
00066 void AudioInputALSA::Close(void)
00067 {
00068 if (pcm_handle != NULL)
00069 {
00070 Stop();
00071 AlsaBad(snd_pcm_close(pcm_handle), "Close close failed");
00072 }
00073 pcm_handle = NULL;
00074 }
00075
00076 bool AudioInputALSA::Stop(void)
00077 {
00078 bool stopped = false;
00079 if (pcm_handle != NULL &&
00080 !AlsaBad(snd_pcm_drop(pcm_handle), "Stop drop failed"))
00081 {
00082 stopped = true;
00083 LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "capture stopped");
00084 }
00085 return stopped;
00086 }
00087
00088 int AudioInputALSA::GetSamples(void* buf, uint nbytes)
00089 {
00090 if (!pcm_handle)
00091 return 0;
00092 int bytes_read = 0;
00093 int pcm_state = snd_pcm_state(pcm_handle);
00094 switch (pcm_state)
00095 {
00096 case SND_PCM_STATE_XRUN:
00097 case SND_PCM_STATE_SUSPENDED:
00098 {
00099 bool recov = Stop() && Start();
00100 LOG(VB_AUDIO, LOG_INFO, LOC_DEV + "xrun recovery " +
00101 (recov ? "good" : "not good"));
00102 if (!recov)
00103 break;
00104 }
00105 case SND_PCM_STATE_PREPARED:
00106 if (AlsaBad(snd_pcm_start(pcm_handle), "pcm start failed"))
00107 break;
00108
00109 case SND_PCM_STATE_RUNNING:
00110 bytes_read = PcmRead(buf, nbytes);
00111 break;
00112 default:
00113 LOG(VB_AUDIO, LOG_ERR, LOC_DEV +
00114 QString("weird pcm state through GetSamples, %1")
00115 .arg(pcm_state));
00116 break;
00117 }
00118 return bytes_read;
00119 }
00120
00121 int AudioInputALSA::GetNumReadyBytes(void)
00122 {
00123 int bytes_avail = 0;
00124 if (pcm_handle != NULL)
00125 {
00126 snd_pcm_sframes_t frames_avail;
00127 int pcm_state = snd_pcm_state(pcm_handle);
00128 switch (pcm_state)
00129 {
00130 case SND_PCM_STATE_PREPARED:
00131 case SND_PCM_STATE_RUNNING:
00132 if (!AlsaBad((frames_avail = snd_pcm_avail_update(pcm_handle)),
00133 "GetNumReadyBytes, available update failed"))
00134 bytes_avail = snd_pcm_frames_to_bytes(pcm_handle,
00135 frames_avail);
00136 }
00137 }
00138 return bytes_avail;
00139 }
00140
00141 bool AudioInputALSA::PrepHwParams(void)
00142 {
00143 snd_pcm_hw_params_t* hwparams;
00144 snd_pcm_hw_params_alloca(&hwparams);
00145 if (AlsaBad(snd_pcm_hw_params_any(pcm_handle, hwparams),
00146 "failed to init hw params"))
00147 return false;
00148 snd_pcm_access_t axs = SND_PCM_ACCESS_RW_INTERLEAVED;
00149 if (AlsaBad(snd_pcm_hw_params_set_access(pcm_handle, hwparams, axs),
00150 "failed to set interleaved rw io"))
00151 return false;
00152 snd_pcm_format_t format =
00153 (m_audio_sample_bits > 8) ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8;
00154 if (AlsaBad(snd_pcm_hw_params_set_format(pcm_handle, hwparams, format),
00155 QString("failed to set sample format %1")
00156 .arg(snd_pcm_format_description(format))))
00157 return false;
00158 if (VERBOSE_LEVEL_CHECK(VB_AUDIO, LOG_DEBUG))
00159 {
00160 uint min_chans, max_chans;
00161 if(AlsaBad(snd_pcm_hw_params_get_channels_min(hwparams, &min_chans),
00162 QString("unable to get min channel count")))
00163 min_chans = 0;
00164 if(AlsaBad(snd_pcm_hw_params_get_channels_max(hwparams, &max_chans),
00165 QString("unable to get max channel count")))
00166 max_chans = 0;
00167 LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV +
00168 QString("min channels %1, max channels %2, myth requests %3")
00169 .arg(min_chans).arg(max_chans).arg(m_audio_channels));
00170 }
00171 if (AlsaBad(snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
00172 m_audio_channels), QString("failed to set channels to %1")
00173 .arg(m_audio_channels)))
00174 {
00175 return false;
00176 }
00177 if (AlsaBad(snd_pcm_hw_params_set_rate(pcm_handle, hwparams,
00178 m_audio_sample_rate, 0), QString("failed to set sample rate %1")
00179 .arg(m_audio_sample_rate)))
00180 {
00181 uint rate_num = 0;
00182 uint rate_den = 0;
00183 if (!AlsaBad(snd_pcm_hw_params_get_rate_numden(hwparams, &rate_num,
00184 &rate_den), "snd_pcm_hw_params_get_rate_numden failed"))
00185 if (m_audio_sample_rate != (int)(rate_num / rate_den))
00186 LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
00187 QString("device reports sample rate as %1")
00188 .arg(rate_num / rate_den));
00189 return false;
00190 }
00191 uint buffer_time = 64000;
00192 uint period_time = buffer_time / 4;
00193 if (AlsaBad(snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams, &period_time, NULL),
00194 "failed to set period time"))
00195 return false;
00196 if (AlsaBad(snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffer_time, NULL),
00197 "failed to set buffer time"))
00198 return false;
00199 if (AlsaBad(snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL),
00200 "failed to get period size"))
00201 return false;
00202
00203 if (AlsaBad(snd_pcm_hw_params (pcm_handle, hwparams),
00204 "failed to set hwparams"))
00205 return false;
00206
00207 myth_block_bytes = snd_pcm_frames_to_bytes(pcm_handle, period_size);
00208 LOG(VB_AUDIO, LOG_INFO, LOC_DEV +
00209 QString("channels %1, sample rate %2, buffer_time %3 msec, period "
00210 "size %4").arg(m_audio_channels)
00211 .arg(m_audio_sample_rate).arg(buffer_time / 1000.0, -1, 'f', 1)
00212 .arg(period_size));
00213 LOG(VB_AUDIO, LOG_DEBUG, LOC_DEV + QString("myth block size %1")
00214 .arg(myth_block_bytes));
00215 return true;
00216 }
00217
00218 bool AudioInputALSA::PrepSwParams(void)
00219 {
00220 snd_pcm_sw_params_t* swparams;
00221 snd_pcm_sw_params_alloca(&swparams);
00222 snd_pcm_uframes_t boundary;
00223 if (AlsaBad(snd_pcm_sw_params_current(pcm_handle, swparams),
00224 "failed to get swparams"))
00225 return false;
00226 if (AlsaBad(snd_pcm_sw_params_get_boundary(swparams, &boundary),
00227 "failed to get boundary"))
00228 return false;
00229
00230 if (AlsaBad(snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams,
00231 boundary), "failed to set start threshold"))
00232 return false;
00233 if (AlsaBad(snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams,
00234 boundary), "failed to set stop threshold"))
00235 return false;
00236 if (AlsaBad(snd_pcm_sw_params(pcm_handle, swparams),
00237 "failed to set software parameters"))
00238 return false;
00239
00240 return true;
00241 }
00242
00243 int AudioInputALSA::PcmRead(void* buf, uint nbytes)
00244 {
00245 unsigned char* bufptr = (unsigned char*)buf;
00246 snd_pcm_uframes_t to_read = snd_pcm_bytes_to_frames(pcm_handle, nbytes);
00247 snd_pcm_uframes_t nframes = to_read;
00248 snd_pcm_sframes_t nread, avail;
00249 int retries = 0;
00250 while (nframes > 0 && retries < 3)
00251 {
00252 if (AlsaBad((avail = snd_pcm_avail_update(pcm_handle)),
00253 "available update failed"))
00254 {
00255 if (!Recovery(avail))
00256 {
00257 ++retries;
00258 continue;
00259 }
00260 }
00261 if ((nread = snd_pcm_readi(pcm_handle, bufptr, nframes)) < 0)
00262 {
00263 switch (nread)
00264 {
00265 case -EAGAIN:
00266 break;
00267 case -EBADFD:
00268 LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
00269 QString("in a state unfit to read (%1): %2")
00270 .arg(nread).arg(snd_strerror(nread)));
00271 break;
00272 case -EINTR:
00273 case -EPIPE:
00274 case -ESTRPIPE:
00275 Recovery(nread);
00276 break;
00277 default:
00278 LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
00279 QString("weird return from snd_pcm_readi: %1")
00280 .arg(snd_strerror(nread)));
00281 break;
00282 }
00283 }
00284 else
00285 {
00286 nframes -= nread;
00287 bufptr += snd_pcm_frames_to_bytes(pcm_handle, nread);
00288 }
00289 ++retries;
00290 }
00291 if (nframes > 0)
00292 LOG(VB_AUDIO, LOG_ERR, LOC_DEV +
00293 QString("short pcm read, %1 of %2 frames, retries %1")
00294 .arg(to_read - nframes).arg(to_read).arg(retries));
00295 return snd_pcm_frames_to_bytes(pcm_handle, to_read - nframes);
00296 }
00297
00298 bool AudioInputALSA::Recovery(int err)
00299 {
00300 if (err > 0)
00301 err = -err;
00302 bool isgood = false;
00303 bool suspense = false;
00304 switch (err)
00305 {
00306 case -EINTR:
00307 isgood = true;
00308 break;
00309 case -ESTRPIPE:
00310 suspense = true;
00311 case -EPIPE:
00312 {
00313 int ret = snd_pcm_prepare(pcm_handle);
00314 if (ret < 0)
00315 {
00316 LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
00317 QString("failed to recover from %1. %2")
00318 .arg(suspense ? "suspend" : "underrun")
00319 .arg(snd_strerror(ret)));
00320 return false;
00321 }
00322 isgood = true;
00323 break;
00324 }
00325 default:
00326 break;
00327 }
00328 return isgood;
00329 }
00330
00331 bool AudioInputALSA::AlsaBad(int op_result, QString errmsg)
00332 {
00333 bool bad = (op_result < 0);
00334 if (bad)
00335 LOG(VB_GENERAL, LOG_ERR, LOC_DEV +
00336 errmsg + ": " + snd_strerror(op_result));
00337 return bad;
00338 }
00339
00340