Commit 1808a81d authored by Ghislain MARY's avatar Ghislain MARY
Browse files

Support QSA audio playback.

parent b64713d9
......@@ -22,6 +22,7 @@
#include "mediastreamer-config.h"
#endif
#include <errno.h>
#include <sys/asoundlib.h>
#include "mediastreamer2/msfilter.h"
......@@ -30,6 +31,10 @@
extern MSFilterDesc ms_qsa_read_desc;
extern MSFilterDesc ms_qsa_write_desc;
/**
* Definition of the private data structure of the QSA playback filter.
*/
......@@ -49,14 +54,17 @@ static void ms_qsa_read_init(MSFilter *f) {
static void ms_qsa_read_preprocess(MSFilter *f) {
/* TODO */
ms_error("ms_qsa_read_preprocess");
}
static void ms_qsa_read_process(MSFilter *f) {
/* TODO */
ms_error("ms_qsa_read_process");
}
static void ms_qsa_read_postprocess(MSFilter *f) {
/* TODO */
ms_error("ms_qsa_read_postprocess");
}
static void ms_qsa_read_uninit(MSFilter *f) {
......@@ -71,21 +79,25 @@ static void ms_qsa_read_uninit(MSFilter *f) {
static int ms_qsa_read_set_sample_rate(MSFilter *f, void *arg) {
/* TODO */
ms_error("ms_qsa_read_set_sample_rate");
return 0;
}
static int ms_qsa_read_get_sample_rate(MSFilter *f, void *arg) {
/* TODO */
ms_error("ms_qsa_read_get_sample_rate");
return 0;
}
static int ms_qsa_read_set_nchannels(MSFilter *f, void *arg) {
/* TODO */
ms_error("ms_qsa_read_set_nchannels");
return 0;
}
static int ms_qsa_read_get_nchannels(MSFilter *f, void *arg) {
/* TODO */
ms_error("ms_qsa_read_get_nchannels");
return 0;
}
......@@ -158,33 +170,177 @@ MS_FILTER_DESC_EXPORT(ms_qsa_read_desc)
* Definition of the private data structure of the QSA playback filter.
*/
typedef struct _MSQSAWriteData {
char some_value;
snd_pcm_channel_info_t info;
snd_pcm_t *handle;
char *pcmdev;
int rate;
int nchannels;
bool_t initialized;
} MSQSAWriteData;
/******************************************************************************
* Methods to (de)initialize and run the QSA playback filter *
*****************************************************************************/
static void ms_qsa_write_init(MSFilter *f) {
MSQSAWriteData *d = (MSQSAWriteData *)ms_new(MSQSAWriteData, 1);
d->some_value = 42;
f->data = d;
static MSFilter * ms_qsa_write_new(MSSndCard *card) {
MSFilter *f = ms_filter_new_from_desc(&ms_qsa_write_desc);
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
snd_pcm_t *handle;
int err;
d->pcmdev = ms_strdup(card->name);
err = snd_pcm_open_name(&handle, d->pcmdev, SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK);
if (err == 0) {
memset(&d->info, 0, sizeof(d->info));
d->info.channel = SND_PCM_CHANNEL_PLAYBACK;
err = snd_pcm_channel_info(handle, &d->info);
if (err == 0) {
ms_error("subname=%s\n"
"\tflags=%u\n"
"\tformats=%u\n"
"\trates=%u\n"
"\tmin_rate=%d\n"
"\tmax_rate=%d\n"
"\tmin_voices=%d\n"
"\tmax_voices=%d\n"
"\tmax_buffer_size=%d\n"
"\tmin_fragment_size=%d\n"
"\tmax_fragment_size=%d\n"
"\tfragment_align=%d\n"
"\tfifo_size=%d\n"
"\ttransfer_block_size=%d\n",
d->info.subname,
d->info.flags,
d->info.formats,
d->info.rates,
d->info.min_rate,
d->info.max_rate,
d->info.min_voices,
d->info.max_voices,
d->info.max_buffer_size,
d->info.min_fragment_size,
d->info.max_fragment_size,
d->info.fragment_align,
d->info.fifo_size,
d->info.transfer_block_size
);
d->nchannels = d->info.max_voices;
d->rate = d->info.max_rate;
d->initialized = TRUE;
}
snd_pcm_close(handle);
}
return f;
}
static void ms_qsa_write_preprocess(MSFilter *f) {
/* TODO */
static void ms_qsa_write_init(MSFilter *f) {
MSQSAWriteData *d = (MSQSAWriteData *)ms_new0(MSQSAWriteData, 1);
f->data = d;
}
static void ms_qsa_write_process(MSFilter *f) {
/* TODO */
snd_pcm_channel_info_t info;
snd_pcm_channel_params_t params;
snd_pcm_channel_status_t status;
mblk_t *im = NULL;
int size;
int written;
int err;
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
if (d->initialized != TRUE) goto setup_failure;
if ((d->handle == NULL) && (d->pcmdev != NULL)) {
err = snd_pcm_open_name(&d->handle, d->pcmdev, SND_PCM_OPEN_PLAYBACK);
if (err != 0) {
ms_error("%s: snd_pcm_open_name(%s) failed: %d", __FUNCTION__, d->pcmdev, err);
goto setup_failure;
}
memset(&info, 0, sizeof(info));
info.channel = SND_PCM_CHANNEL_PLAYBACK;
err = snd_pcm_plugin_info(d->handle, &info);
if (err != 0) {
ms_error("%s: snd_pcm_plugin_info() failed: %d", __FUNCTION__, err);
goto setup_failure;
}
memset(&params, 0, sizeof(params));
params.channel = SND_PCM_CHANNEL_PLAYBACK;
params.mode = SND_PCM_MODE_BLOCK;
params.start_mode = SND_PCM_START_DATA;
params.stop_mode = SND_PCM_STOP_STOP;
params.buf.block.frag_size = info.max_fragment_size;
params.buf.block.frags_min = 1;
params.buf.block.frags_max = -1;
params.format.interleave = 1;
params.format.rate = d->rate;
params.format.voices = d->nchannels;
params.format.format = SND_PCM_SFMT_S16_LE;
err = snd_pcm_plugin_params(d->handle, &params);
if (err != 0) {
ms_error("%s: snd_pcm_plugin_params() failed: %d", __FUNCTION__, err);
goto setup_failure;
}
err = snd_pcm_plugin_prepare(d->handle, SND_PCM_CHANNEL_PLAYBACK);
if (err != 0) {
ms_error("%s: snd_pcm_plugin_prepare() failed: %d", __FUNCTION__, err);
goto setup_failure;
}
}
if (d->handle == NULL) goto setup_failure;
while ((im = ms_queue_get(f->inputs[0])) != NULL) {
while((size = im->b_wptr - im->b_rptr) > 0) {
written = snd_pcm_plugin_write(d->handle, im->b_rptr, size);
if (written < size) {
ms_warning("%s: snd_pcm_plugin_write(%d) failed: %d", __FUNCTION__, size, errno);
memset(&status, 0, sizeof(status));
status.channel = SND_PCM_CHANNEL_PLAYBACK;
err = snd_pcm_plugin_status(d->handle, &status);
if (err != 0) {
ms_error("%s: snd_pcm_plugin_status() failed", __FUNCTION__);
goto setup_failure;
}
if ((status.status == SND_PCM_STATUS_READY) || (status.status == SND_PCM_STATUS_UNDERRUN)) {
err = snd_pcm_plugin_prepare(d->handle, SND_PCM_CHANNEL_PLAYBACK);
if (err != 0) {
ms_error("%s: snd_pcm_plugin_prepare() failed", __FUNCTION__);
goto setup_failure;
}
}
if (written < 0) written = 0;
}
im->b_rptr += written;
}
freemsg(im);
}
return;
setup_failure:
if (d->handle != NULL) {
snd_pcm_close(d->handle);
d->handle = NULL;
}
ms_queue_flush(f->inputs[0]);
}
static void ms_qsa_write_postprocess(MSFilter *f) {
/* TODO */
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
if (d->handle != NULL) {
snd_pcm_plugin_flush(d->handle, SND_PCM_CHANNEL_PLAYBACK);
snd_pcm_close(d->handle);
d->handle = NULL;
}
}
static void ms_qsa_write_uninit(MSFilter *f) {
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
if (d->pcmdev != NULL) {
ms_free(d->pcmdev);
d->pcmdev = NULL;
}
ms_free(d);
}
......@@ -193,23 +349,65 @@ static void ms_qsa_write_uninit(MSFilter *f) {
* Methods to configure the QSA playback filter *
*****************************************************************************/
#define DEF_RATE_MAP(rate) { rate, SND_PCM_RATE_ ## rate }
struct _rate_map {
int hz;
uint32_t pcm_rate;
};
static struct _rate_map rate_map[] = {
DEF_RATE_MAP(8000),
DEF_RATE_MAP(11025),
DEF_RATE_MAP(16000),
DEF_RATE_MAP(22050),
DEF_RATE_MAP(32000),
DEF_RATE_MAP(44100),
DEF_RATE_MAP(48000),
{ 0, 0 }
};
static int ms_qsa_write_set_sample_rate(MSFilter *f, void *arg) {
/* TODO */
return 0;
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
int rate = *((int *)arg);
uint32_t pcm_rate;
struct _rate_map *rm = &rate_map[0];
ms_error("ms_qsa_write_set_sample_rate: %d", rate);
while (rm->hz != 0) {
if (rm->hz == rate) break;
rm++;
}
if (rm->hz != 0) {
ms_error("\trate = %d", rate);
d->rate = rate;
return 0;
}
return -1;
}
static int ms_qsa_write_get_sample_rate(MSFilter *f, void *arg) {
/* TODO */
MSQSAWriteData * d = (MSQSAWriteData *)f->data;
ms_error("ms_qsa_write_get_sample_rate: %d", d->rate);
*((int*)arg) = d->rate;
return 0;
}
static int ms_qsa_write_set_nchannels(MSFilter *f, void *arg) {
/* TODO */
return 0;
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
int nchannels = *((int *)arg);
ms_error("ms_qsa_write_set_nchannels: %d", nchannels);
if ((nchannels >= d->info.min_voices) && (nchannels <= d->info.max_voices)) {
d->nchannels = nchannels;
return 0;
}
return -1;
}
static int ms_qsa_write_get_nchannels(MSFilter *f, void *arg) {
/* TODO */
MSQSAWriteData *d = (MSQSAWriteData *)f->data;
ms_error("ms_qsa_write_get_nchannels: %d", d->nchannels);
*((int *)arg) = d->nchannels;
return 0;
}
......@@ -245,7 +443,6 @@ MSFilterDesc ms_qsa_write_desc = {
.ninputs = MS_QSA_WRITE_NINPUTS,
.noutputs = MS_QSA_WRITE_NOUTPUTS,
.init = ms_qsa_write_init,
.preprocess = ms_qsa_write_preprocess,
.process = ms_qsa_write_process,
.postprocess = ms_qsa_write_postprocess,
.uninit = ms_qsa_write_uninit,
......@@ -264,7 +461,7 @@ MSFilterDesc ms_qsa_write_desc = {
MS_QSA_WRITE_NINPUTS,
MS_QSA_WRITE_NOUTPUTS,
ms_qsa_write_init,
ms_qsa_write_preprocess,
NULL,
ms_qsa_write_process,
ms_qsa_write_postprocess,
ms_qsa_write_uninit,
......@@ -292,6 +489,11 @@ static MSFilter * ms_qsa_card_create_writer(MSSndCard *card);
static void ms_qsa_card_uninit(MSSndCard *card);
static MSSndCard * ms_qsa_card_duplicate(MSSndCard *card);
typedef struct _MSQSAData {
char *pcmdev;
char *mixdev;
} MSQSAData;
#ifndef _MSC_VER
MSSndCardDesc ms_qsa_card_desc = {
......@@ -331,8 +533,36 @@ MSSndCardDesc ms_qsa_card_desc = {
#endif
static void ms_qsa_card_detect(MSSndCardManager *m) {
/* TODO */
snd_pcm_t *handle_play = NULL;
snd_pcm_t *handle_capt = NULL;
MSSndCard *card;
int err;
ms_error("ms_qsa_card_detect");
err = snd_pcm_open_name(&handle_play, "tones", SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK);
if (err == 0) {
snd_pcm_close(handle_play);
card = ms_snd_card_new(&ms_qsa_card_desc);
if (card != NULL) {
card->name = ms_strdup("tones");
card->capabilities = MS_SND_CARD_CAP_PLAYBACK;
ms_snd_card_manager_add_card(m, card);
}
}
err = snd_pcm_open_name(&handle_play, "voice", SND_PCM_OPEN_PLAYBACK | SND_PCM_OPEN_NONBLOCK);
if (err == 0) {
err = snd_pcm_open_name(&handle_capt, "voice", SND_PCM_OPEN_CAPTURE | SND_PCM_OPEN_NONBLOCK);
if (err == 0) {
snd_pcm_close(handle_capt);
card = ms_snd_card_new(&ms_qsa_card_desc);
if (card != NULL) {
card->name = ms_strdup("voice");
card->capabilities = MS_SND_CARD_CAPTURE | MS_SND_CARD_PLAYBACK;
ms_snd_card_manager_add_card(m, card);
}
}
snd_pcm_close(handle_play);
}
}
static void ms_qsa_card_init(MSSndCard *card) {
......@@ -363,9 +593,7 @@ static MSFilter * ms_qsa_card_create_reader(MSSndCard *card) {
}
static MSFilter * ms_qsa_card_create_writer(MSSndCard *card) {
/* TODO */
ms_error("ms_qsa_card_create_writer");
return NULL;
return ms_qsa_write_new(card);
}
static void ms_qsa_card_uninit(MSSndCard *card) {
......
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