Commit b8393f4e authored by Mikhail Karpenko's avatar Mikhail Karpenko
Browse files

Stream audio over one channel only

The port number for audio streaming is passed in command-line arguments.
The sound device can also be specified on command line and default
device now is set to plughw:0,0 due to some problems with time stamps
("default" device did not produce system time stamps).
parent 3b899fc7
Loading
Loading
Loading
Loading
+18 −11
Original line number Original line Diff line number Diff line
@@ -55,10 +55,11 @@
#define SAMPLE_TIME               20                            //< restrict ALSA to have this period, in milliseconds
#define SAMPLE_TIME               20                            //< restrict ALSA to have this period, in milliseconds
#define BUFFER_TIME               1000                          //< approximate buffer duration for ALSA, in milliseconds
#define BUFFER_TIME               1000                          //< approximate buffer duration for ALSA, in milliseconds
#define LEN                       1200                          //< the size of data buffer for RTP packet, in bytes
#define LEN                       1200                          //< the size of data buffer for RTP packet, in bytes
#define DEFAULT_SND_DEVICE        "plughw:0,0"                  //< default sound card


using namespace std;
using namespace std;


Audio::Audio(int port, bool enable, Parameters *pars, int sample_rate, int channels) {
Audio::Audio(int port, bool enable, Parameters *pars, string &dev_name, int sample_rate, int channels) {
	snd_pcm_hw_params_t *hw_params;
	snd_pcm_hw_params_t *hw_params;
	snd_pcm_sw_params_t *sw_params;
	snd_pcm_sw_params_t *sw_params;
	_present = false;
	_present = false;
@@ -101,8 +102,17 @@ Audio::Audio(int port, bool enable, Parameters *pars, int sample_rate, int chann
		sbuffer = new short[sbuffer_len * 2 * _channels];
		sbuffer = new short[sbuffer_len * 2 * _channels];
		packet_buffer = new unsigned char[LEN + 20];
		packet_buffer = new unsigned char[LEN + 20];
		bool init_ok = false;
		bool init_ok = false;

		if (dev_name == "")
			dev_name = DEFAULT_SND_DEVICE;
		while (true) {
		while (true) {
			if ((err = snd_pcm_open(&capture_handle, "default", SND_PCM_STREAM_CAPTURE, 0)) < 0)
			/* Previously, "default" device was used in snd_pcm_open(), but this resulted in incorrect time stamps
			 * reported by snd_pcm_status_get_tstamp() (probably a bug in ALSA plugins). The time stamps did not correspond
			 * to system time, but rather started from 0 after reboot and then increased. Opening "plughw:0,0" device
			 * produces correct time stamps, thus this is the default device name now and it can be set from command line.
			 */
			D2(sensor_port, cerr << "Trying to open " << dev_name << endl);
			if ((err = snd_pcm_open(&capture_handle, dev_name.c_str(), SND_PCM_STREAM_CAPTURE, 0)) < 0)
				break;
				break;
			snd_pcm_hw_params_alloca(&hw_params);
			snd_pcm_hw_params_alloca(&hw_params);
			if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
			if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
@@ -133,11 +143,7 @@ Audio::Audio(int port, bool enable, Parameters *pars, int sample_rate, int chann
				break;
				break;
			if ((err = snd_pcm_sw_params_set_tstamp_mode(capture_handle, sw_params, SND_PCM_TSTAMP_ENABLE)) < 0)
			if ((err = snd_pcm_sw_params_set_tstamp_mode(capture_handle, sw_params, SND_PCM_TSTAMP_ENABLE)) < 0)
				break;
				break;
			/* SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY for some reason does not produce time stamps equal to system time,
			if ((err = snd_pcm_sw_params_set_tstamp_type(capture_handle, sw_params, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)) < 0)
			 * so stick with monotonic time stamps and apply the difference between FPGA time and ALSA time stamps to
			 * the current samples during transmission
			 */
			if ((err = snd_pcm_sw_params_set_tstamp_type(capture_handle, sw_params, SND_PCM_TSTAMP_TYPE_MONOTONIC)) < 0)
				break;
				break;
			if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
			if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
				break;
				break;
@@ -153,6 +159,7 @@ Audio::Audio(int port, bool enable, Parameters *pars, int sample_rate, int chann
			init_pthread((void *) this);
			init_pthread((void *) this);
		} else {
		} else {
			D(sensor_port, cerr << "Audio: init FAIL!" << endl);
			D(sensor_port, cerr << "Audio: init FAIL!" << endl);
			D(sensor_port, cerr << "ALSA error message: " << snd_strerror(err) << endl);
			_present = false;
			_present = false;
		}
		}
	}
	}
@@ -224,8 +231,8 @@ void Audio::set_capture_volume(int nvolume) {
 * @brief Start audio stream if it was enabled
 * @brief Start audio stream if it was enabled
 * The time delta between FPGA time and time stamps reported by ALSA is calculated before stream is started.
 * The time delta between FPGA time and time stamps reported by ALSA is calculated before stream is started.
 * This delta is applied to time stamps received from ALSA. Previously, this delta was calculated as time difference
 * This delta is applied to time stamps received from ALSA. Previously, this delta was calculated as time difference
 * between FPGA time and system time reported by \e getsystimeofday(), but as for now ALSA reports monotonic
 * between FPGA time and system time reported by \e getsystimeofday(), but this was changed because of some problems
 * time stamps regardless of the time stamp type set by \e snd_pcm_sw_params_set_tstamp_type(). See git commit
 * with getting system time stamps from sound device opened as "default" ("plughw" is used now). See git commit
 * 58b954f239b695b7deda7a33657841d2c64476ae (or earlier) for previous implementation.
 * 58b954f239b695b7deda7a33657841d2c64476ae (or earlier) for previous implementation.
 * @param   ip   ip/port pair for socket
 * @param   ip   ip/port pair for socket
 * @param   port ip/port pair for socket
 * @param   port ip/port pair for socket
+1 −4
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@ using namespace std;


class Audio : public RTP_Stream {
class Audio : public RTP_Stream {
public:
public:
	Audio(int port, bool enable, Parameters *pars, int sample_rate = SAMPLE_RATE, int channels = SAMPLE_CHANNELS);
	Audio(int port, bool enable, Parameters *pars, string &dev_name, int sample_rate = SAMPLE_RATE, int channels = SAMPLE_CHANNELS);
	virtual ~Audio(void);
	virtual ~Audio(void);
	long sample_rate(void) { return _sample_rate; };
	long sample_rate(void) { return _sample_rate; };
	long channels(void) { return _channels; };
	long channels(void) { return _channels; };
@@ -51,7 +51,6 @@ public:
	void Start(string ip, long port, int ttl = -1);
	void Start(string ip, long port, int ttl = -1);
	void Stop(void);
	void Stop(void);
protected:
protected:
//	int fd;
	snd_pcm_t *capture_handle;                                  //< PCM handle, returned from snd_pcm_open
	snd_pcm_t *capture_handle;                                  //< PCM handle, returned from snd_pcm_open
	short *sbuffer;                                             //< buffer for sound data
	short *sbuffer;                                             //< buffer for sound data
	long sbuffer_len;
	long sbuffer_len;
@@ -67,8 +66,6 @@ protected:


	uint64_t timestamp_rtcp;
	uint64_t timestamp_rtcp;
	long delta_fpga_sys;	                                    //< A/V clocks delta for RTCP
	long delta_fpga_sys;	                                    //< A/V clocks delta for RTCP
//	bool is_first;
//	bool is_first2;
	long long delta_fpga_alsa;                                  //< time delta between FPGA time and time reported by ALSA, in microseconds
	long long delta_fpga_alsa;                                  //< time delta between FPGA time and time reported by ALSA, in microseconds
	unsigned char *packet_buffer;                               //< buffer for RTP packet data (header plus PCM samples)
	unsigned char *packet_buffer;                               //< buffer for RTP packet data (header plus PCM samples)
private:
private:
+37 −3
Original line number Original line Diff line number Diff line
@@ -47,13 +47,33 @@ void clean_up(pthread_t *threads, size_t sz) {
	}
	}
}
}


/**
 * Print help message on stdout.
 * @param   argv   a list of command-line arguments, used to get the name of application
 * @return  None
 */
void print_help(char *argv[])
{
	const char *msg = "Simple RTSP streamer implementation for Elphel393 series cameras.\n"
			"Usage: %s [-s <port>][-D <sound_card>][-f <fps>][-h], where\n\n"
			"\t-h\t\tprint this help message;\n"
			"\t-s <port>\tstream sound from USB microphone over sensor port <port> channel. By default, audio streaming is not\n"
			"\t\t\tenabled if this option is not specified;\n"
			"\t-D <sound_card>\tuse <sound_card> device for sound stream input. The default device is plughw:0,0;\n"
			"\t-f <fps>\tlimit frames per second for video streaming, works for free running mode only.\n"; // this one is processed in streamer class
	printf(msg, argv[0]);
}

int main(int argc, char *argv[]) {
int main(int argc, char *argv[]) {
	int ret_val;
	int ret_val;
	int audio_port = -1;
	string opt;
	string opt;
	map<string, string> args;
	map<string, string> args;
	map<string, string>::iterator args_it;
	pthread_t threads[SENSOR_PORTS];
	pthread_t threads[SENSOR_PORTS];
	Streamer *streamers[SENSOR_PORTS] = {NULL};
	Streamer *streamers[SENSOR_PORTS] = {NULL};


	// copy command-line arguments to a map container for further processing in streamer class
	for (int i = 1; i < argc; i++) {
	for (int i = 1; i < argc; i++) {
		if (argv[i][0] == '-' && argv[i][1] != '\0') {
		if (argv[i][0] == '-' && argv[i][1] != '\0') {
			if (opt != "")
			if (opt != "")
@@ -72,12 +92,26 @@ int main(int argc, char *argv[]) {
	for (map<string, string>::iterator it = args.begin(); it != args.end(); it++) {
	for (map<string, string>::iterator it = args.begin(); it != args.end(); it++) {
		cerr << "|" << (*it).first << "| == |" << (*it).second << "|" << endl;
		cerr << "|" << (*it).first << "| == |" << (*it).second << "|" << endl;
	}
	}
	if ((args_it = args.find("h")) != args.end()) {
		print_help(argv);
		exit(EXIT_SUCCESS);
	} else if ((args_it = args.find("s")) != args.end()) {
		audio_port = strtol(args_it->second.c_str(), NULL, 10);
		// sanity check, invalid conversion produces 0 which is fine
		if (audio_port < 0 || audio_port >= SENSOR_PORTS)
			audio_port = -1;
	}


	for (int i = 0; i < 1; i++) {
	for (int i = 0; i < SENSOR_PORTS; i++) {
//	for (int i = 0; i < SENSOR_PORTS; i++) {
		bool audio_en;
		pthread_attr_t attr;
		pthread_attr_t attr;

		if (i == audio_port)
			audio_en = true;
		else
			audio_en = false;
		cout << "Start thread " << i << endl;
		cout << "Start thread " << i << endl;
		streamers[i] = new Streamer(args, i);
		streamers[i] = new Streamer(args, i, audio_en);


		pthread_attr_init(&attr);
		pthread_attr_init(&attr);
		ret_val = pthread_create(&threads[i], &attr, Streamer::pthread_f, (void *) streamers[i]);
		ret_val = pthread_create(&threads[i], &attr, Streamer::pthread_f, (void *) streamers[i]);
+19 −17
Original line number Original line Diff line number Diff line
@@ -56,16 +56,14 @@ using namespace std;
	#define D2(s_port, a)
	#define D2(s_port, a)
#endif
#endif


//Streamer *Streamer::_streamer = NULL;
Streamer::Streamer(const map<string, string> &_args, int port_num, bool audio_en) {

Streamer::Streamer(const map<string, string> &_args, int port_num) {
	sensor_port = port_num;
	sensor_port = port_num;
	_streamer = this;
	_streamer = this;
	session = new Session();
	session = new Session();
	params = new Parameters(sensor_port);
	params = new Parameters(sensor_port);
	args = _args;
	args = _args;
	audio = NULL;
	audio = NULL;
	session->process_audio = true;
	session->process_audio = audio_en;
	session->audio.sample_rate = 0;
	session->audio.sample_rate = 0;
	session->audio.channels = 0;
	session->audio.channels = 0;
	session->rtp_out.ip_custom = false;
	session->rtp_out.ip_custom = false;
@@ -98,7 +96,12 @@ void Streamer::audio_init(void) {
		delete audio;
		delete audio;
	}
	}
	D(sensor_port, cout << "audio_enabled == " << session->process_audio << endl);
	D(sensor_port, cout << "audio_enabled == " << session->process_audio << endl);
	audio = new Audio(sensor_port, session->process_audio, params, session->audio.sample_rate, session->audio.channels);
	if (session->process_audio) {
		string dev_name = "";
		map<string, string>::iterator args_it;
		if ((args_it = args.find("D")) != args.end())
			dev_name = args_it->second;
		audio = new Audio(sensor_port, session->process_audio, params, dev_name, session->audio.sample_rate, session->audio.channels);
		if (audio->present() && session->process_audio) {
		if (audio->present() && session->process_audio) {
			session->process_audio = true;
			session->process_audio = true;
			session->audio.type = audio->ptype();
			session->audio.type = audio->ptype();
@@ -111,6 +114,7 @@ void Streamer::audio_init(void) {
			session->audio.channels = 0;
			session->audio.channels = 0;
		}
		}
	}
	}
}


Streamer::~Streamer(void) {
Streamer::~Streamer(void) {
	delete video;
	delete video;
@@ -278,8 +282,6 @@ int Streamer::update_settings(bool apply) {
		f_audio_channels = true;
		f_audio_channels = true;
	if ((audio_proc || session->process_audio) && (f_audio_rate || f_audio_channels))
	if ((audio_proc || session->process_audio) && (f_audio_rate || f_audio_channels))
		audio_was_changed = true;
		audio_was_changed = true;
	D(sensor_port, cerr << "audio_proc = " << audio_proc << ", process_audio = " << session->process_audio << ", f_audio_rate = " << f_audio_rate
			<< ", f_audio_channels = " << f_audio_channels << endl);
	if (apply) {
	if (apply) {
		bool audio_restarted = false;
		bool audio_restarted = false;
		if (audio_was_changed) {
		if (audio_was_changed) {
+1 −1
Original line number Original line Diff line number Diff line
@@ -33,7 +33,7 @@ using namespace std;


class Streamer {
class Streamer {
public:
public:
	Streamer(const map<string, string> &args, int port_num);
	Streamer(const map<string, string> &args, int port_num, bool audio_en = false);
	~Streamer();
	~Streamer();
	void Main(void);
	void Main(void);
	bool opt_present(string name) {
	bool opt_present(string name) {