diff --git a/client/alsaPlayer.cpp b/client/alsaPlayer.cpp index c85f878f..dcf187ed 100644 --- a/client/alsaPlayer.cpp +++ b/client/alsaPlayer.cpp @@ -25,15 +25,15 @@ using namespace std; -Player::Player(const PcmDevice& pcmDevice, Stream* stream) : pcm_handle_(NULL), buff_(NULL), active_(false), stream_(stream), pcmDevice_(pcmDevice) +Player::Player(const PcmDevice& pcmDevice, Stream* stream) : buff_(NULL), active_(false), stream_(stream), pcmDevice_(pcmDevice) { } -void Player::start() +snd_pcm_t* Player::initAlsa() { - unsigned int pcm, tmp, rate; - int channels; + unsigned int tmp, rate; + int pcm, channels; snd_pcm_hw_params_t *params; int buff_size; @@ -41,10 +41,14 @@ void Player::start() rate = format.rate; channels = format.channels; + snd_pcm_t* pcm_handle(NULL); + /* Open the PCM device in playback mode */ - if ((pcm = snd_pcm_open(&pcm_handle_, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) + if ((pcm = snd_pcm_open(&pcm_handle, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) + { logE << "ERROR: Can't open " << pcmDevice_.name << " PCM device. " << snd_strerror(pcm) << "\n"; - + return NULL; + } /* struct snd_pcm_playback_info_t pinfo; if ( (pcm = snd_pcm_playback_info( pcm_handle, &pinfo )) < 0 ) fprintf( stderr, "Error: playback info error: %s\n", snd_strerror( err ) ); @@ -53,20 +57,32 @@ void Player::start() /* Allocate parameters object and fill it with default values*/ snd_pcm_hw_params_alloca(¶ms); - snd_pcm_hw_params_any(pcm_handle_, params); + snd_pcm_hw_params_any(pcm_handle, params); /* Set parameters */ - if ((pcm = snd_pcm_hw_params_set_access(pcm_handle_, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + if ((pcm = snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) + { logE << "ERROR: Can't set interleaved mode. " << snd_strerror(pcm) << "\n"; + return NULL; + } - if ((pcm = snd_pcm_hw_params_set_format(pcm_handle_, params, SND_PCM_FORMAT_S16_LE)) < 0) + if ((pcm = snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE)) < 0) + { logE << "ERROR: Can't set format. " << snd_strerror(pcm) << "\n"; + return NULL; + } - if ((pcm = snd_pcm_hw_params_set_channels(pcm_handle_, params, channels)) < 0) + if ((pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels)) < 0) + { logE << "ERROR: Can't set channels number. " << snd_strerror(pcm) << "\n"; + return NULL; + } - if ((pcm = snd_pcm_hw_params_set_rate_near(pcm_handle_, params, &rate, 0)) < 0) + if ((pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0)) < 0) + { logE << "ERROR: Can't set rate. " << snd_strerror(pcm) << "\n"; + return NULL; + } unsigned int buffer_time; snd_pcm_hw_params_get_buffer_time_max(params, &buffer_time, 0); @@ -75,20 +91,23 @@ void Player::start() unsigned int period_time = buffer_time / 4; - snd_pcm_hw_params_set_period_time_near(pcm_handle_, params, &period_time, 0); - snd_pcm_hw_params_set_buffer_time_near(pcm_handle_, params, &buffer_time, 0); + snd_pcm_hw_params_set_period_time_near(pcm_handle, params, &period_time, 0); + snd_pcm_hw_params_set_buffer_time_near(pcm_handle, params, &buffer_time, 0); // long unsigned int periodsize = stream_->format.msRate() * 50;//2*rate/50; // if ((pcm = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, params, &periodsize)) < 0) // logE << "Unable to set buffer size " << (long int)periodsize << ": " << snd_strerror(pcm) << "\n"; /* Write parameters */ - if ((pcm = snd_pcm_hw_params(pcm_handle_, params)) < 0) + if ((pcm = snd_pcm_hw_params(pcm_handle, params)) < 0) + { logE << "ERROR: Can't set harware parameters. " << snd_strerror(pcm) << "\n"; + return NULL; + } /* Resume information */ - logD << "PCM name: " << snd_pcm_name(pcm_handle_) << "\n"; - logD << "PCM state: " << snd_pcm_state_name(snd_pcm_state(pcm_handle_)) << "\n"; + logD << "PCM name: " << snd_pcm_name(pcm_handle) << "\n"; + logD << "PCM state: " << snd_pcm_state_name(snd_pcm_state(pcm_handle)) << "\n"; snd_pcm_hw_params_get_channels(params, &tmp); logD << "channels: " << tmp << "\n"; @@ -107,13 +126,34 @@ void Player::start() snd_pcm_sw_params_t *swparams; snd_pcm_sw_params_alloca(&swparams); - snd_pcm_sw_params_current(pcm_handle_, swparams); + snd_pcm_sw_params_current(pcm_handle, swparams); - snd_pcm_sw_params_set_avail_min(pcm_handle_, swparams, frames_); - snd_pcm_sw_params_set_start_threshold(pcm_handle_, swparams, frames_); + snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, frames_); + snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, frames_); // snd_pcm_sw_params_set_stop_threshold(pcm_handle, swparams, frames_); - snd_pcm_sw_params(pcm_handle_, swparams); + snd_pcm_sw_params(pcm_handle, swparams); + return pcm_handle; +} + +void Player::uninitAlsa(snd_pcm_t* handle) +{ + if (handle != NULL) + { + snd_pcm_drain(handle); + snd_pcm_close(handle); + } + + if (buff_ != NULL) + { + free(buff_); + buff_ = NULL; + } +} + + +void Player::start() +{ active_ = true; playerThread_ = thread(&Player::worker, this); } @@ -125,49 +165,49 @@ Player::~Player() } -void Player::stop() { +void Player::stop() +{ if (active_) { active_ = false; playerThread_.join(); } - - if (pcm_handle_ != NULL) - { - snd_pcm_drain(pcm_handle_); - snd_pcm_close(pcm_handle_); - pcm_handle_ = NULL; - } - - if (buff_ != NULL) - { - free(buff_); - buff_ = NULL; - } } void Player::worker() { - unsigned int pcm; + snd_pcm_t* handle = NULL; + snd_pcm_sframes_t pcm; snd_pcm_sframes_t framesAvail; snd_pcm_sframes_t framesDelay; while (active_) { - snd_pcm_avail_delay(pcm_handle_, &framesAvail, &framesDelay); + if (handle == NULL) + { + if ((handle = initAlsa()) == NULL) + { + usleep(100*1000); + continue; + } + } + + snd_pcm_avail_delay(handle, &framesAvail, &framesDelay); chronos::usec delay((chronos::usec::rep) (1000 * (double) framesDelay / stream_->getFormat().msRate())); logD << "Avail: " << framesAvail << ", delay: " << framesDelay << ", delay[ms]: " << delay.count() / 1000 << "\n"; if (stream_->getPlayerChunk(buff_, delay, frames_)) { - if ((pcm = snd_pcm_writei(pcm_handle_, buff_, frames_)) == -EPIPE) + if ((pcm = snd_pcm_writei(handle, buff_, frames_)) == -EPIPE) { logE << "XRUN\n"; - snd_pcm_prepare(pcm_handle_); + snd_pcm_prepare(handle); } else if (pcm < 0) { logE << "ERROR. Can't write to PCM device: " << snd_strerror(pcm) << "\n"; + uninitAlsa(handle); + handle = NULL; } } else @@ -176,6 +216,7 @@ void Player::worker() usleep(100*1000); } } + uninitAlsa(handle); } diff --git a/client/alsaPlayer.h b/client/alsaPlayer.h index 61c5025c..f8512b0e 100644 --- a/client/alsaPlayer.h +++ b/client/alsaPlayer.h @@ -38,8 +38,9 @@ public: static std::vector pcm_list(void); private: + snd_pcm_t* initAlsa(); + void uninitAlsa(snd_pcm_t* handle); void worker(); - snd_pcm_t* pcm_handle_; snd_pcm_uframes_t frames_; char *buff_; std::atomic active_; diff --git a/client/stream.cpp b/client/stream.cpp index 5e517e0a..da9836c9 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -243,7 +243,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT { logO << "sleep > bufferDuration/2: " << cs::duration(sleep_) << " > " << cs::duration(bufferDuration)/2 << "\n"; // We're late: discard oldest chunks - do + while (sleep_ > chunk_->duration()) { logO << "sleep > chunkDuration: " << cs::duration(sleep_) << " > " << chunk_->duration().count() << ", chunks: " << chunks_.size() << ", out: " << cs::duration(outputBufferDacTime) << ", needed: " << cs::duration(bufferDuration) << "\n"; sleep_ = std::chrono::duration_cast(TimeProvider::serverNow() - chunk_->start() - bufferMs_ + outputBufferDacTime); @@ -255,7 +255,6 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT return false; } } - while (sleep_ > chunk_->duration()); } // out of sync, can be corrected by playing faster/slower