示例#1
0
文件: ALSAInput.cpp 项目: KarolS/ssr
static void ALSARecoverAfterOverrun(snd_pcm_t* pcm) {
	Logger::LogWarning("[ALSARecoverAfterOverrun] " + QObject::tr("Warning: An overrun has occurred, some samples were lost.", "Don't translate 'overrun'"));
	if(snd_pcm_prepare(pcm) < 0) {
		Logger::LogError("[ALSARecoverAfterOverrun] " + QObject::tr("Error: Can't recover device after overrun!", "Don't translate 'overrun'"));
		throw ALSAException();
	}
	if(snd_pcm_start(pcm) < 0) {
		Logger::LogError("[ALSARecoverAfterOverrun] " + QObject::tr("Error: Can't start PCM device after overrun!", "Don't translate 'overrun'"));
		throw ALSAException();
	}
}
示例#2
0
文件: ALSAInput.cpp 项目: KarolS/ssr
void ALSAInput::Init() {

	snd_pcm_hw_params_t *alsa_hw_params = NULL;

	try {

		// allocate parameter structure
		if(snd_pcm_hw_params_malloc(&alsa_hw_params) < 0) {
			throw std::bad_alloc();
		}

		// open PCM device
		if(snd_pcm_open(&m_alsa_pcm, m_device_name.toAscii().constData(), SND_PCM_STREAM_CAPTURE, 0) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't open PCM device!"));
			throw ALSAException();
		}
		if(snd_pcm_hw_params_any(m_alsa_pcm, alsa_hw_params) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't get PCM hardware parameters!"));
			throw ALSAException();
		}

		// set access type
		if(snd_pcm_hw_params_set_access(m_alsa_pcm, alsa_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set access type!"));
			throw ALSAException();
		}

		// set sample format
		if(snd_pcm_hw_params_set_format(m_alsa_pcm, alsa_hw_params, SND_PCM_FORMAT_S16_LE) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set sample format!"));
			throw ALSAException();
		}

		// set sample rate
		unsigned int rate = m_sample_rate;
		if(snd_pcm_hw_params_set_rate_near(m_alsa_pcm, alsa_hw_params, &rate, NULL) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set sample rate!"));
			throw ALSAException();
		}
		if(rate != m_sample_rate) {
			Logger::LogWarning("[ALSAInput::Init] " + QObject::tr("Warning: Sample rate %1 is not supported, using %2 instead. "
																  "This could be a problem if the difference is large.")
							   .arg(m_sample_rate).arg(rate));
			//TODO// enable once resampling is ready
			//m_sample_rate = rate;
		}

		// set channel count
		if(snd_pcm_hw_params_set_channels(m_alsa_pcm, alsa_hw_params, m_channels) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set channel count!"));
			throw ALSAException();
		}

		// set period count
		unsigned int periods = m_alsa_periods;
		if(snd_pcm_hw_params_set_periods_near(m_alsa_pcm, alsa_hw_params, &periods, NULL) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set period count!"));
			throw ALSAException();
		}
		if(periods != m_alsa_periods) {
			Logger::LogWarning("[ALSAInput::Init] " + QObject::tr("Warning: Period count %1 is not supported, using %2 instead. "
																  "This is not a problem.")
							   .arg(m_alsa_periods).arg(periods));
			m_alsa_periods = periods;
		}

		// set period size
		snd_pcm_uframes_t period_size = m_alsa_period_size;
		if(snd_pcm_hw_params_set_period_size_near(m_alsa_pcm, alsa_hw_params, &period_size, NULL) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't set period size!"));
			throw ALSAException();
		}
		if(period_size != m_alsa_period_size) {
			Logger::LogWarning("[ALSAInput::Init] " + QObject::tr("Warning: Period size %1 is not supported, using %2 instead. "
																  "This is not a problem.")
							   .arg(m_alsa_period_size).arg(period_size));
			m_alsa_period_size = period_size;
		}

		// apply parameters
		if(snd_pcm_hw_params(m_alsa_pcm, alsa_hw_params) < 0) {
			Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't apply PCM hardware parameters!"));
			throw ALSAException();
		}

		// free parameter structure
		snd_pcm_hw_params_free(alsa_hw_params);
		alsa_hw_params = NULL;

	} catch(...) {
		if(alsa_hw_params != NULL) {
			snd_pcm_hw_params_free(alsa_hw_params);
			alsa_hw_params = NULL;
		}
		throw;
	}

	// start PCM device
	if(snd_pcm_start(m_alsa_pcm) < 0) {
		Logger::LogError("[ALSAInput::Init] " + QObject::tr("Error: Can't start PCM device!"));
		throw ALSAException();
	}

	// start input thread
	m_should_stop = false;
	m_error_occurred = false;
	m_thread = std::thread(&ALSAInput::InputThread, this);

}
示例#3
0
文件: ALSAInput.cpp 项目: KarolS/ssr
void ALSAInput::InputThread() {
	try {

		Logger::LogInfo("[ALSAInput::InputThread] " + QObject::tr("Input thread started."));

		std::vector<uint8_t> buffer(m_alsa_period_size * m_channels * 2);
		bool has_first_samples = false;
		int64_t first_timestamp = 0; // value won't be used, but GCC gives a warning otherwise

		while(!m_should_stop) {

			// wait until samples are available
			// This is not actually required since snd_pcm_readi is blocking, but unlike snd_pcm_read,
			// this function has a timeout value. This means the input thread won't hang if the device turns out to be dead.
			int res = snd_pcm_wait(m_alsa_pcm, 1000);
			if(res == 0) {
				continue;
			}
			if(res < 0) {
				if(res == -EPIPE) {
					ALSARecoverAfterOverrun(m_alsa_pcm);
					PushAudioHole();
				} else {
					Logger::LogError("[ALSAInput::InputThread] " + QObject::tr("Error: Can't check whether samples are available!"));
					throw ALSAException();
				}
				continue;
			}

			// read the samples
			snd_pcm_sframes_t samples_read = snd_pcm_readi(m_alsa_pcm, buffer.data(), m_alsa_period_size);
			if(samples_read < 0) {
				if(samples_read == -EPIPE) {
					ALSARecoverAfterOverrun(m_alsa_pcm);
					PushAudioHole();
				} else {
					Logger::LogError("[ALSAInput::InputThread] " + QObject::tr("Error: Can't read samples!"));
					throw ALSAException();
				}
				continue;
			}
			if(samples_read <= 0)
				continue;

			int64_t timestamp = hrt_time_micro();

			// skip the first samples
			if(has_first_samples) {
				if(timestamp > first_timestamp + START_DELAY) {

					// push the samples
					int64_t time = timestamp - (int64_t) samples_read * (int64_t) 1000000 / (int64_t) m_sample_rate;
					PushAudioSamples(m_sample_rate, m_channels, samples_read, buffer.data(), AV_SAMPLE_FMT_S16, time);

				}
			} else {
				has_first_samples = true;
				first_timestamp = timestamp;
			}

		}

		Logger::LogInfo("[ALSAInput::InputThread] " + QObject::tr("Input thread stopped."));

	} catch(const std::exception& e) {
		m_error_occurred = true;
		Logger::LogError("[ALSAInput::InputThread] " + QObject::tr("Exception '%1' in input thread.").arg(e.what()));
	} catch(...) {
		m_error_occurred = true;
		Logger::LogError("[ALSAInput::InputThread] " + QObject::tr("Unknown exception in input thread."));
	}
}
示例#4
0
文件: ALSAInput.cpp 项目: Regmos/ssr
std::vector<ALSAInput::Source> ALSAInput::GetSourceList() {
	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.
	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
	// 'pulse' is also blacklisted because the native PulseAudio backend is more reliable
	std::vector<std::string> plugin_blacklist = {
		"cards", "default", "sysdefault", "hw", "plughw", "plug", "dmix", "dsnoop", "shm", "tee", "file", "null",
		"front", "rear", "center_lfe", "side", "surround40", "surround41", "surround50", "surround51", "surround71",
		"iec958", "spdif", "hdmi", "modem", "phoneline", "pulse",
	};
	std::sort(plugin_blacklist.begin(), plugin_blacklist.end());

	// the 'default' PCM must be first, so add it explicitly
	list.push_back(Source("default", "Default source"));

	Logger::LogInfo("[ALSAInput::GetSourceList] " + Logger::tr("Generating source list ..."));

	snd_ctl_card_info_t *alsa_card_info = NULL;
	snd_pcm_info_t *alsa_pcm_info = NULL;
	snd_ctl_t *alsa_ctl = NULL;

	try {

		// allocate card and PCM info structure
		if(snd_ctl_card_info_malloc(&alsa_card_info) < 0) {
			throw std::bad_alloc();
		}
		if(snd_pcm_info_malloc(&alsa_pcm_info) < 0) {
			throw std::bad_alloc();
		}

		// update the ALSA configuration
		snd_config_update_free_global();
		if(snd_config_update() < 0) {
			Logger::LogError("[ALSAInput::GetSourceList] " + Logger::tr("Error: Could not update ALSA configuration!"));
			throw ALSAException();
		}

		// find all PCM plugins (by parsing the config file)
		snd_config_t *alsa_config_pcms = NULL;
		if(snd_config_search(snd_config, "pcm", &alsa_config_pcms) == 0) {
			snd_config_iterator_t i, next;
			snd_config_for_each(i, next, alsa_config_pcms) {
				snd_config_t *alsa_config_pcm = snd_config_iterator_entry(i);

				// get the name
				const char *id = NULL;
				if(snd_config_get_id(alsa_config_pcm, &id) < 0 || id == NULL)
					continue;
				std::string plugin_name = id;

				// ignore the plugin if it is blacklisted
				if(std::binary_search(plugin_blacklist.begin(), plugin_blacklist.end(), plugin_name))
					continue;

				// try to get the description
				std::string plugin_description;
				snd_config_t *alsa_config_description = NULL;
				if(snd_config_search(alsa_config_pcm, "hint.description", &alsa_config_description) == 0) {
					const char *str = NULL;
					if(snd_config_get_string(alsa_config_description, &str) >= 0 && str != NULL) {
						plugin_description = str;
					}
				}

				// if there is no description, ignore it, because it's probably not meant to be used
				if(plugin_description.empty())
					continue;

				// if there is no description, use the type instead
				/*if(plugin_description.empty()) {
					snd_config_t *alsa_config_type = NULL;
					if(snd_config_search(alsa_config_pcm, "type", &alsa_config_type) >= 0) {
						const char *str = NULL;
						if(snd_config_get_string(alsa_config_type, &str) >= 0 && str != NULL) {
							plugin_description = std::string(str) + " plugin";
						}
					}
				}*/

				// add to list
				Logger::LogInfo("[ALSAInput::GetSourceList] " + Logger::tr("Found plugin: [%1] %2").arg(QString::fromStdString(plugin_name)).arg(QString::fromStdString(plugin_description)));
				list.push_back(Source(plugin_name, plugin_description));

			}
		}