"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/AV/Input/ALSAInput.cpp" between
ssr-0.4.1.tar.gz and ssr-0.4.2.tar.gz

About: SimpleScreenRecorder is a screen (video-audio) recorder for Linux.

ALSAInput.cpp  (ssr-0.4.1):ALSAInput.cpp  (ssr-0.4.2)
skipping to change at line 26 skipping to change at line 26
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with SimpleScreenRecorder. If not, see <http://www.gnu.org/licenses/>. along with SimpleScreenRecorder. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "ALSAInput.h" #include "ALSAInput.h"
#if SSR_USE_ALSA #if SSR_USE_ALSA
#include "Logger.h" #include "Logger.h"
#include "TempBuffer.h"
// Artificial delay after the first samples have been received (in microseconds) . Any samples received during this time will be dropped. // Artificial delay after the first samples have been received (in microseconds) . Any samples received during this time will be dropped.
// This is needed because the first samples sometimes have weird timestamps, esp ecially when PulseAudio is active // This is needed because the first samples sometimes have weird timestamps, esp ecially when PulseAudio is active
// (I've seen one situation where PulseAudio instantly 'captures' 2 seconds of s ilence when the recording is started). // (I've seen one situation where PulseAudio instantly 'captures' 2 seconds of s ilence when the recording is started).
// It also eliminates the clicking sound when the microphone is started for the first time. // It also eliminates the clicking sound when the microphone is started for the first time.
const int64_t ALSAInput::START_DELAY = 100000; const int64_t ALSAInput::START_DELAY = 100000;
static void ALSARecoverAfterOverrun(snd_pcm_t* pcm) { static void ALSARecoverAfterOverrun(snd_pcm_t* pcm) {
Logger::LogWarning("[ALSARecoverAfterOverrun] " + Logger::tr("Warning: An overrun has occurred, some samples were lost.", "Don't translate 'overrun'")); Logger::LogWarning("[ALSARecoverAfterOverrun] " + Logger::tr("Warning: An overrun has occurred, some samples were lost.", "Don't translate 'overrun'"));
if(snd_pcm_prepare(pcm) < 0) { if(snd_pcm_prepare(pcm) < 0) {
Logger::LogError("[ALSARecoverAfterOverrun] " + Logger::tr("Error : Can't recover device after overrun!", "Don't translate 'overrun'")); Logger::LogError("[ALSARecoverAfterOverrun] " + Logger::tr("Error : Can't recover device after overrun!", "Don't translate 'overrun'"));
skipping to change at line 48 skipping to change at line 50
if(snd_pcm_start(pcm) < 0) { if(snd_pcm_start(pcm) < 0) {
Logger::LogError("[ALSARecoverAfterOverrun] " + Logger::tr("Error : Can't start PCM device after overrun!", "Don't translate 'overrun'")); Logger::LogError("[ALSARecoverAfterOverrun] " + Logger::tr("Error : Can't start PCM device after overrun!", "Don't translate 'overrun'"));
throw ALSAException(); throw ALSAException();
} }
} }
ALSAInput::ALSAInput(const QString& source_name, unsigned int sample_rate) { ALSAInput::ALSAInput(const QString& source_name, unsigned int sample_rate) {
m_source_name = source_name; m_source_name = source_name;
m_sample_rate = sample_rate; m_sample_rate = sample_rate;
m_sample_format = AV_SAMPLE_FMT_S16; // default, may change later
m_convert_24_to_32 = false;
m_channels = 2; // always 2 channels because the synchronizer and encoder don't support anything else at this point m_channels = 2; // always 2 channels because the synchronizer and encoder don't support anything else at this point
m_period_size = 1024; // number of samples per period m_period_size = 1024; // number of samples per period
m_buffer_size = m_period_size * 8; // number of samples in the buffer m_buffer_size = m_period_size * 8; // number of samples in the buffer
m_alsa_pcm = NULL; m_alsa_pcm = NULL;
try { try {
Init(); Init();
} catch(...) { } catch(...) {
Free(); Free();
skipping to change at line 86 skipping to change at line 90
std::vector<ALSAInput::Source> ALSAInput::GetSourceList() { std::vector<ALSAInput::Source> ALSAInput::GetSourceList() {
std::vector<Source> list; std::vector<Source> list;
/* /*
This code is based on the ALSA device detection code used in PortAudio. I just ported it to C++ and improved it a bit. This code is based on the ALSA device detection code used in PortAudio. I just ported it to C++ and improved it a bit.
All credit goes to the PortAudio devs, they saved me a lot of time :). All credit goes to the PortAudio devs, they saved me a lot of time :).
*/ */
// these ALSA plugins are blacklisted because they are always defined but rarely useful // these ALSA plugins are blacklisted because they are always defined but rarely useful
// 'pulse' is also blacklisted because the native PulseAudio backend is m ore reliable // 'pulse' and 'jack' are also blacklisted because the native backends ar e more reliable
std::vector<std::string> plugin_blacklist = { std::vector<std::string> plugin_blacklist = {
"cards", "default", "sysdefault", "hw", "plughw", "plug", "dmix", "dsnoop", "shm", "tee", "file", "null", "cards", "default", "sysdefault", "hw", "plughw", "plug", "dmix", "dsnoop", "shm", "tee", "file", "null",
"front", "rear", "center_lfe", "side", "surround40", "surround41" , "surround50", "surround51", "surround71", "front", "rear", "center_lfe", "side", "surround40", "surround41" , "surround50", "surround51", "surround71",
"iec958", "spdif", "hdmi", "modem", "phoneline", "pulse", "iec958", "spdif", "hdmi", "modem", "phoneline", "oss", "pulse",
"jack", "speex", "speexrate", "samplerate",
"upmix", "vdownmix", "usbstream",
}; };
std::sort(plugin_blacklist.begin(), plugin_blacklist.end()); std::sort(plugin_blacklist.begin(), plugin_blacklist.end());
// the 'default' PCM must be first, so add it explicitly // the 'default' PCM must be first, so add it explicitly
list.push_back(Source("default", "Default source")); list.push_back(Source("default", "Default source"));
Logger::LogInfo("[ALSAInput::GetSourceList] " + Logger::tr("Generating so urce list ...")); Logger::LogInfo("[ALSAInput::GetSourceList] " + Logger::tr("Generating so urce list ..."));
snd_ctl_card_info_t *alsa_card_info = NULL; snd_ctl_card_info_t *alsa_card_info = NULL;
snd_pcm_info_t *alsa_pcm_info = NULL; snd_pcm_info_t *alsa_pcm_info = NULL;
skipping to change at line 250 skipping to change at line 255
} }
// don't re-throw exception // don't re-throw exception
} }
return list; return list;
} }
void ALSAInput::Init() { void ALSAInput::Init() {
snd_pcm_hw_params_t *alsa_hw_params = NULL; snd_pcm_hw_params_t *alsa_hw_params = NULL;
snd_pcm_format_mask_t *alsa_format_mask = NULL;
try { try {
// allocate parameter structure // allocate parameter structure
if(snd_pcm_hw_params_malloc(&alsa_hw_params) < 0) { if(snd_pcm_hw_params_malloc(&alsa_hw_params) < 0) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
// allocate format mask structure
if(snd_pcm_format_mask_malloc(&alsa_format_mask) < 0) {
throw std::bad_alloc();
}
// open PCM device // open PCM device
if(snd_pcm_open(&m_alsa_pcm, m_source_name.toUtf8().constData(), SND_PCM_STREAM_CAPTURE, 0) < 0) { if(snd_pcm_open(&m_alsa_pcm, m_source_name.toUtf8().constData(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't open PCM device!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't open PCM device!"));
throw ALSAException(); throw ALSAException();
} }
if(snd_pcm_hw_params_any(m_alsa_pcm, alsa_hw_params) < 0) { if(snd_pcm_hw_params_any(m_alsa_pcm, alsa_hw_params) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't get PCM hardware parameters!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't get PCM hardware parameters!"));
throw ALSAException(); throw ALSAException();
} }
// set access type // set access type
if(snd_pcm_hw_params_set_access(m_alsa_pcm, alsa_hw_params, SND_P CM_ACCESS_RW_INTERLEAVED) < 0) { if(snd_pcm_hw_params_set_access(m_alsa_pcm, alsa_hw_params, SND_P CM_ACCESS_RW_INTERLEAVED) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't set access type!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't set access type!"));
throw ALSAException(); throw ALSAException();
} }
// set sample format // set sample format
if(snd_pcm_hw_params_set_format(m_alsa_pcm, alsa_hw_params, SND_P snd_pcm_format_mask_none(alsa_format_mask);
CM_FORMAT_S16_LE) < 0) { snd_pcm_format_mask_set(alsa_format_mask, SND_PCM_FORMAT_S16_LE);
snd_pcm_format_mask_set(alsa_format_mask, SND_PCM_FORMAT_S24_LE);
snd_pcm_format_mask_set(alsa_format_mask, SND_PCM_FORMAT_S32_LE);
snd_pcm_format_mask_set(alsa_format_mask, SND_PCM_FORMAT_FLOAT_LE
);
if(snd_pcm_hw_params_set_format_mask(m_alsa_pcm, alsa_hw_params,
alsa_format_mask) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error
: Can't set sample format mask!"));
throw ALSAException();
}
snd_pcm_format_t sample_format;
if(snd_pcm_hw_params_set_format_first(m_alsa_pcm, alsa_hw_params,
&sample_format) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't set sample format!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't set sample format!"));
throw ALSAException(); throw ALSAException();
} }
const char *format_str = NULL;
switch(sample_format) {
case SND_PCM_FORMAT_S16_LE: {
m_sample_format = AV_SAMPLE_FMT_S16;
format_str = "s16";
break;
}
case SND_PCM_FORMAT_S24_LE: {
m_sample_format = AV_SAMPLE_FMT_S32;
m_convert_24_to_32 = true;
format_str = "s24";
break;
}
case SND_PCM_FORMAT_S32_LE: {
m_sample_format = AV_SAMPLE_FMT_S32;
format_str = "s32";
break;
}
case SND_PCM_FORMAT_FLOAT_LE: {
m_sample_format = AV_SAMPLE_FMT_FLT;
format_str = "f32";
break;
}
default: assert(false);
}
Logger::LogInfo("[ALSAInput::InputThread] " + Logger::tr("Using s
ample format %1.").arg(format_str));
// set sample rate // set sample rate
unsigned int rate = m_sample_rate; unsigned int rate = m_sample_rate;
if(snd_pcm_hw_params_set_rate_near(m_alsa_pcm, alsa_hw_params, &r ate, NULL) < 0) { if(snd_pcm_hw_params_set_rate_near(m_alsa_pcm, alsa_hw_params, &r ate, NULL) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't set sample rate!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't set sample rate!"));
throw ALSAException(); throw ALSAException();
} }
if(rate != m_sample_rate) { if(rate != m_sample_rate) {
Logger::LogWarning("[ALSAInput::Init] " + Logger::tr("War ning: Sample rate %1 is not supported, using %2 instead. " Logger::LogWarning("[ALSAInput::Init] " + Logger::tr("War ning: Sample rate %1 is not supported, using %2 instead. "
"This is not a problem.") "This is not a problem.")
skipping to change at line 338 skipping to change at line 385
.arg(m_buffer_size).ar g(buffer_size)); .arg(m_buffer_size).ar g(buffer_size));
m_buffer_size = buffer_size; m_buffer_size = buffer_size;
} }
// apply parameters // apply parameters
if(snd_pcm_hw_params(m_alsa_pcm, alsa_hw_params) < 0) { if(snd_pcm_hw_params(m_alsa_pcm, alsa_hw_params) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't apply PCM hardware parameters!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error : Can't apply PCM hardware parameters!"));
throw ALSAException(); throw ALSAException();
} }
// free format mask structure
snd_pcm_format_mask_free(alsa_format_mask);
alsa_format_mask = NULL;
// free parameter structure // free parameter structure
snd_pcm_hw_params_free(alsa_hw_params); snd_pcm_hw_params_free(alsa_hw_params);
alsa_hw_params = NULL; alsa_hw_params = NULL;
} catch(...) { } catch(...) {
if(alsa_format_mask != NULL) {
snd_pcm_format_mask_free(alsa_format_mask);
alsa_format_mask = NULL;
}
if(alsa_hw_params != NULL) { if(alsa_hw_params != NULL) {
snd_pcm_hw_params_free(alsa_hw_params); snd_pcm_hw_params_free(alsa_hw_params);
alsa_hw_params = NULL; alsa_hw_params = NULL;
} }
throw; throw;
} }
// start PCM device // start PCM device
if(snd_pcm_start(m_alsa_pcm) < 0) { if(snd_pcm_start(m_alsa_pcm) < 0) {
Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error: Can't start PCM device!")); Logger::LogError("[ALSAInput::Init] " + Logger::tr("Error: Can't start PCM device!"));
skipping to change at line 375 skipping to change at line 430
snd_pcm_close(m_alsa_pcm); snd_pcm_close(m_alsa_pcm);
m_alsa_pcm = NULL; m_alsa_pcm = NULL;
} }
} }
void ALSAInput::InputThread() { void ALSAInput::InputThread() {
try { try {
Logger::LogInfo("[ALSAInput::InputThread] " + Logger::tr("Input t hread started.")); Logger::LogInfo("[ALSAInput::InputThread] " + Logger::tr("Input t hread started."));
std::vector<uint16_t> buffer(m_period_size * m_channels); // allocate buffer
TempBuffer<uint8_t> buffer;
switch(m_sample_format) {
case AV_SAMPLE_FMT_S16: buffer.Alloc(m_period_size * m_ch
annels * sizeof(int16_t)); break;
case AV_SAMPLE_FMT_S32: buffer.Alloc(m_period_size * m_ch
annels * sizeof(int32_t)); break;
case AV_SAMPLE_FMT_FLT: buffer.Alloc(m_period_size * m_ch
annels * sizeof(float)); break;
default: assert(false);
}
bool has_first_samples = false; bool has_first_samples = false;
int64_t first_timestamp = 0; // value won't be used, but GCC give s a warning otherwise int64_t first_timestamp = 0; // value won't be used, but GCC give s a warning otherwise
while(!m_should_stop) { while(!m_should_stop) {
// wait for new samples
int wait = snd_pcm_wait(m_alsa_pcm, 100);
if(wait < 0) {
if(wait == -EPIPE) {
ALSARecoverAfterOverrun(m_alsa_pcm);
PushAudioHole();
continue;
} else {
Logger::LogError("[ALSAInput::InputThread
] " + Logger::tr("Error: Can't wait for new samples!"));
throw ALSAException();
}
} else if(wait == 0) {
continue;
}
int64_t timestamp = hrt_time_micro();
// read the samples // read the samples
snd_pcm_sframes_t samples_read = snd_pcm_readi(m_alsa_pcm , buffer.data(), m_period_size); snd_pcm_sframes_t samples_read = snd_pcm_readi(m_alsa_pcm , buffer.GetData(), m_period_size);
if(samples_read < 0) { if(samples_read < 0) {
if(samples_read == -EPIPE) { if(samples_read == -EPIPE) {
ALSARecoverAfterOverrun(m_alsa_pcm); ALSARecoverAfterOverrun(m_alsa_pcm);
PushAudioHole(); PushAudioHole();
continue;
} else { } else {
Logger::LogError("[ALSAInput::InputThread ] " + Logger::tr("Error: Can't read samples!")); Logger::LogError("[ALSAInput::InputThread ] " + Logger::tr("Error: Can't read samples!"));
throw ALSAException(); throw ALSAException();
} }
} else if(samples_read == 0) {
continue; continue;
} }
if(samples_read <= 0)
continue;
int64_t timestamp = hrt_time_micro();
// skip the first samples // skip the first samples
if(has_first_samples) { if(has_first_samples) {
if(timestamp > first_timestamp + START_DELAY) { if(timestamp > first_timestamp + START_DELAY) {
// convert if needed
if(m_convert_24_to_32) {
int32_t *samples = (int32_t*) buf
fer.GetData();
for(unsigned int i = 0; i < (unsi
gned int) samples_read * m_channels; ++i) {
samples[i] <<= 8;
}
}
// push the samples // push the samples
int64_t time = timestamp - (int64_t) samp les_read * (int64_t) 1000000 / (int64_t) m_sample_rate; int64_t time = timestamp - (int64_t) samp les_read * (int64_t) 1000000 / (int64_t) m_sample_rate;
PushAudioSamples(m_channels, m_sample_rat e, AV_SAMPLE_FMT_S16, samples_read, (uint8_t*) buffer.data(), time); PushAudioSamples(m_channels, m_sample_rat e, m_sample_format, samples_read, buffer.GetData(), time);
} }
} else { } else {
has_first_samples = true; has_first_samples = true;
first_timestamp = timestamp; first_timestamp = timestamp;
} }
} }
Logger::LogInfo("[ALSAInput::InputThread] " + Logger::tr("Input t hread stopped.")); Logger::LogInfo("[ALSAInput::InputThread] " + Logger::tr("Input t hread stopped."));
 End of changes. 19 change blocks. 
12 lines changed or deleted 109 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)