Commit b8393f4e authored by Mikhail Karpenko's avatar Mikhail Karpenko

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
......@@ -52,13 +52,14 @@
#define D2(s_port, a)
#endif
#define SAMPLE_TIME 20 //< restrict ALSA to have this period, in milliseconds
#define BUFFER_TIME 1000 //< approximate buffer duration for ALSA, 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 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;
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_sw_params_t *sw_params;
_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];
packet_buffer = new unsigned char[LEN + 20];
bool init_ok = false;
if (dev_name == "")
dev_name = DEFAULT_SND_DEVICE;
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;
snd_pcm_hw_params_alloca(&hw_params);
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;
if ((err = snd_pcm_sw_params_set_tstamp_mode(capture_handle, sw_params, SND_PCM_TSTAMP_ENABLE)) < 0)
break;
/* SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY for some reason does not produce time stamps equal to system time,
* 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)
if ((err = snd_pcm_sw_params_set_tstamp_type(capture_handle, sw_params, SND_PCM_TSTAMP_TYPE_GETTIMEOFDAY)) < 0)
break;
if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
break;
......@@ -153,6 +159,7 @@ Audio::Audio(int port, bool enable, Parameters *pars, int sample_rate, int chann
init_pthread((void *) this);
} else {
D(sensor_port, cerr << "Audio: init FAIL!" << endl);
D(sensor_port, cerr << "ALSA error message: " << snd_strerror(err) << endl);
_present = false;
}
}
......@@ -224,8 +231,8 @@ void Audio::set_capture_volume(int nvolume) {
* @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.
* 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
* time stamps regardless of the time stamp type set by \e snd_pcm_sw_params_set_tstamp_type(). See git commit
* between FPGA time and system time reported by \e getsystimeofday(), but this was changed because of some problems
* with getting system time stamps from sound device opened as "default" ("plughw" is used now). See git commit
* 58b954f239b695b7deda7a33657841d2c64476ae (or earlier) for previous implementation.
* @param ip ip/port pair for socket
* @param port ip/port pair for socket
......
......@@ -40,7 +40,7 @@ using namespace std;
class Audio : public RTP_Stream {
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);
long sample_rate(void) { return _sample_rate; };
long channels(void) { return _channels; };
......@@ -51,7 +51,6 @@ public:
void Start(string ip, long port, int ttl = -1);
void Stop(void);
protected:
// int fd;
snd_pcm_t *capture_handle; //< PCM handle, returned from snd_pcm_open
short *sbuffer; //< buffer for sound data
long sbuffer_len;
......@@ -67,8 +66,6 @@ protected:
uint64_t timestamp_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
unsigned char *packet_buffer; //< buffer for RTP packet data (header plus PCM samples)
private:
......
......@@ -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 ret_val;
int audio_port = -1;
string opt;
map<string, string> args;
map<string, string>::iterator args_it;
pthread_t threads[SENSOR_PORTS];
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++) {
if (argv[i][0] == '-' && argv[i][1] != '\0') {
if (opt != "")
......@@ -72,12 +92,26 @@ int main(int argc, char *argv[]) {
for (map<string, string>::iterator it = args.begin(); it != args.end(); it++) {
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;
if (i == audio_port)
audio_en = true;
else
audio_en = false;
cout << "Start thread " << i << endl;
streamers[i] = new Streamer(args, i);
streamers[i] = new Streamer(args, i, audio_en);
pthread_attr_init(&attr);
ret_val = pthread_create(&threads[i], &attr, Streamer::pthread_f, (void *) streamers[i]);
......
......@@ -56,16 +56,14 @@ using namespace std;
#define D2(s_port, a)
#endif
//Streamer *Streamer::_streamer = NULL;
Streamer::Streamer(const map<string, string> &_args, int port_num) {
Streamer::Streamer(const map<string, string> &_args, int port_num, bool audio_en) {
sensor_port = port_num;
_streamer = this;
session = new Session();
params = new Parameters(sensor_port);
args = _args;
audio = NULL;
session->process_audio = true;
session->process_audio = audio_en;
session->audio.sample_rate = 0;
session->audio.channels = 0;
session->rtp_out.ip_custom = false;
......@@ -98,17 +96,23 @@ void Streamer::audio_init(void) {
delete audio;
}
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 (audio->present() && session->process_audio) {
session->process_audio = true;
session->audio.type = audio->ptype();
session->audio.sample_rate = audio->sample_rate();
session->audio.channels = audio->channels();
} else {
session->process_audio = false;
session->audio.type = -1;
session->audio.sample_rate = 0;
session->audio.channels = 0;
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) {
session->process_audio = true;
session->audio.type = audio->ptype();
session->audio.sample_rate = audio->sample_rate();
session->audio.channels = audio->channels();
} else {
session->process_audio = false;
session->audio.type = -1;
session->audio.sample_rate = 0;
session->audio.channels = 0;
}
}
}
......@@ -278,8 +282,6 @@ int Streamer::update_settings(bool apply) {
f_audio_channels = true;
if ((audio_proc || session->process_audio) && (f_audio_rate || f_audio_channels))
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) {
bool audio_restarted = false;
if (audio_was_changed) {
......
......@@ -33,7 +33,7 @@ using namespace std;
class Streamer {
public:
Streamer(const map<string, string> &args, int port_num);
Streamer(const map<string, string> &args, int port_num, bool audio_en = false);
~Streamer();
void Main(void);
bool opt_present(string name) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment