Commit 21c9b377 authored by Simon Morlat's avatar Simon Morlat

add pulseaudio support (totally untested)

parent b340a76e
......@@ -407,6 +407,24 @@ AC_SUBST(JACK_LIBS)
fi
AC_ARG_ENABLE(pulseaudio,
[ --disable-pulseaudio Disable pulseaudio support],
[case "${enableval}" in
yes) pulseaudio=true ;;
no) pulseaudio=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --disable-pulseaudio) ;;
esac],[pulseaudio=true])
if test x$pulseaudio = xtrue ; then
PKG_CHECK_MODULES(LIBPULSE, libpulse >= 0.9.21,
[AC_DEFINE(__PULSEAUDIO_ENABLED__,1,[Pulse audio support])],
[pulseaudio=false])
fi
AM_CONDITIONAL(BUILD_PULSEAUDIO,test x$pulseaudio = xtrue)
if test "$found_sound" = "no"; then
AC_MSG_WARN([Could not find a support sound driver API])
fi
......
......@@ -89,6 +89,8 @@ typedef enum MSFilterId{
MS_EQUALIZER_ID,
MS_JPEG_DEC_ID,
MS_JPEG_ENC_ID,
MS_PULSE_READ_ID,
MS_PULSE_WRITE_ID
} MSFilterId;
......
......@@ -88,6 +88,10 @@ if BUILD_MACAQSND
libmediastreamer_la_SOURCES+=aqsnd.c
endif
if BUILD_PULSEAUDIO
libmediastreamer_la_SOURCES+=pulseaudio.c
endif
if BUILD_VIDEO
......@@ -140,6 +144,7 @@ libmediastreamer_la_LIBADD= $(ORTP_LIBS) \
$(PORTAUDIO_LIBS) \
$(ALSA_LIBS) \
$(ARTS_LIBS) \
$(LIBPULSE_LIBS) \
$(SPEEX_LIBS) \
$(GSM_LIBS) \
$(LIBV4L1_LIBS) \
......@@ -172,7 +177,8 @@ AM_CFLAGS= -I$(top_srcdir) \
$(ORTP_CFLAGS) \
$(SPEEX_CFLAGS) \
$(GSM_CFLAGS) \
$(STRICT_OPTIONS)
$(STRICT_OPTIONS) \
$(LIBPULSE_CFLAGS)
AM_CXXFLAGS= -I$(top_srcdir) \
$(ORTP_CFLAGS) \
......
......@@ -431,6 +431,10 @@ extern MSSndCardDesc pasnd_card_desc;
extern MSSndCardDesc aq_card_desc;
#endif
#ifdef __PULSEAUDIO_ENABLED__
extern MSSndCardDesc pulse_card_desc;
#endif
static MSSndCardDesc * ms_snd_card_descs[]={
#ifdef __ALSA_ENABLED__
&alsa_card_desc,
......@@ -455,6 +459,9 @@ static MSSndCardDesc * ms_snd_card_descs[]={
#endif
#ifdef __MAC_AQ_ENABLED__
&aq_card_desc,
#endif
#ifdef __PULSEAUDIO_ENABLED__
&pulse_card_desc,
#endif
NULL
};
......
/*
mediastreamer2 library - modular sound and video processing and streaming
Copyright (C) 2010 Simon MORLAT (simon.morlat@linphone.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "mediastreamer2/msfilter.h"
#include "mediastreamer2/mssndcard.h"
#include <pulse/pulseaudio.h>
static const float latency_req=0.02;
static void init_pulse_context();
static void pulse_card_detect(MSSndCardManager *m);
static MSFilter *pulse_card_create_reader(MSSndCard *card);
static MSFilter *pulse_card_create_writer(MSSndCard *card);
static void pulse_card_init(MSSndCard *obj){
}
static void pulse_card_uninit(MSSndCard *obj){
}
MSSndCardDesc pulse_card_desc={
.driver_type="PulseAudio",
.detect=pulse_card_detect,
.init=pulse_card_init,
.create_reader=pulse_card_create_reader,
.create_writer=pulse_card_create_writer,
.uninit=pulse_card_uninit
};
static void pulse_card_detect(MSSndCardManager *m){
MSSndCard *card=ms_snd_card_new(&pulse_card_desc);
if (card!=NULL){
card->name=ms_strdup("default");
card->capabilities=MS_SND_CARD_CAP_CAPTURE|MS_SND_CARD_CAP_PLAYBACK;
ms_snd_card_manager_add_card(m,card);
init_pulse_context();
}
}
static pa_context *context=NULL;
static pa_threaded_mainloop *pa_loop=NULL;
static bool_t pa_ready=FALSE;
static void uninit_pulse_context(){
pa_context_disconnect(context);
pa_context_unref(context);
pa_threaded_mainloop_stop(pa_loop);
}
static void state_notify(pa_context *ctx, void *userdata){
pa_context_state_t state=pa_context_get_state(ctx);
const char *sname="";
switch (state){
case PA_CONTEXT_UNCONNECTED:
sname="PA_CONTEXT_UNCONNECTED";
break;
case PA_CONTEXT_CONNECTING:
sname="PA_CONTEXT_CONNECTING";
break;
case PA_CONTEXT_AUTHORIZING:
sname="PA_CONTEXT_AUTHORIZING";
break;
case PA_CONTEXT_SETTING_NAME:
sname="PA_CONTEXT_SETTING_NAME";
break;
case PA_CONTEXT_READY:
sname="PA_CONTEXT_READY";
pa_ready=TRUE;
break;
case PA_CONTEXT_FAILED:
sname="PA_CONTEXT_FAILED";
break;
case PA_CONTEXT_TERMINATED:
sname="PA_CONTEXT_TERMINATED";
break;
}
ms_message("New PulseAudio context state: %s",sname);
}
static void init_pulse_context(){
if (context==NULL){
pa_loop=pa_threaded_mainloop_new();
context=pa_context_new(pa_threaded_mainloop_get_api(pa_loop),"mediastreamer2");
pa_context_set_state_callback(context,state_notify,NULL);
pa_context_connect(context,NULL,0,NULL);
pa_threaded_mainloop_start(pa_loop);
atexit(uninit_pulse_context);
}
}
static void check_pulse_ready(){
int i=0;
while(pa_ready==FALSE && i<10){
usleep(20000);
}
}
typedef struct _PulseReadState{
int channels;
int rate;
pa_stream *stream;
int fragsize;
}PulseReadState;
static void pulse_read_init(MSFilter *f){
PulseReadState *s=ms_new0(PulseReadState,1);
s->channels=1;
s->rate=8000;
f->data=s;
check_pulse_ready();
}
static void pulse_read_preprocess(MSFilter *f){
PulseReadState *s=(PulseReadState *)f->data;
int err;
pa_sample_spec pss;
pa_buffer_attr attr;
if (context==NULL) return;
pss.format=PA_SAMPLE_S16LE;
pss.channels=s->channels;
pss.rate=s->rate;
attr.maxlength=-1;
attr.tlength=-1;
attr.prebuf=-1;
attr.minreq=-1;
attr.fragsize=s->fragsize=latency_req*s->channels*s->rate*2;
s->stream=pa_stream_new(context,"mediastreamer2 read filter",&pss,NULL);
if (s->stream==NULL){
ms_error("pa_stream_new() failed: %s",pa_strerror(pa_context_errno(context)));
return;
}
err=pa_stream_connect_record(s->stream,NULL,&attr, PA_STREAM_ADJUST_LATENCY);
if (err!=0){
ms_error("pa_stream_connect_record() failed");
}
}
static void stub(void *p){
}
static void pulse_read_process(MSFilter *f){
PulseReadState *s=(PulseReadState *)f->data;
const void *buffer=NULL;
size_t nbytes=0;
if (s->stream!=NULL){
pa_stream_peek(s->stream,&buffer,&nbytes);
if (buffer!=NULL){
pa_stream_drop(s->stream);
mblk_t *om=esballoc((uint8_t*)buffer,nbytes,0,stub);
om->b_wptr+=nbytes;
ms_queue_put(f->outputs[0],om);
}
}
}
static void pulse_read_postprocess(MSFilter *f){
PulseReadState *s=(PulseReadState *)f->data;
if (s->stream) {
pa_stream_disconnect(s->stream);
pa_stream_unref(s->stream);
}
}
static int pulse_read_set_sr(MSFilter *f, void *arg){
PulseReadState *s=(PulseReadState *)f->data;
s->rate=*(int*)arg;
return 0;
}
static int pulse_read_get_sr(MSFilter *f, void *arg){
PulseReadState *s=(PulseReadState *)f->data;
*(int*)arg=s->rate;
return 0;
}
static int pulse_read_set_nchannels(MSFilter *f, void *arg){
PulseReadState *s=(PulseReadState *)f->data;
s->channels=*(int*)arg;
return 0;
}
static MSFilterMethod pulse_read_methods[]={
{ MS_FILTER_SET_SAMPLE_RATE , pulse_read_set_sr },
{ MS_FILTER_GET_SAMPLE_RATE , pulse_read_get_sr },
{ MS_FILTER_SET_NCHANNELS , pulse_read_set_nchannels },
{ 0 , 0 }
};
static MSFilterDesc pulse_read_desc={
.id=MS_PULSE_READ_ID,
.name="MSPulseRead",
.text="Sound input plugin based on PulseAudio",
.ninputs=0,
.noutputs=1,
.category=MS_FILTER_OTHER,
.init=pulse_read_init,
.preprocess=pulse_read_preprocess,
.process=pulse_read_process,
.postprocess=pulse_read_postprocess,
.methods=pulse_read_methods
};
typedef struct _PulseReadState PulseWriteState;
static void pulse_write_init(MSFilter *f){
PulseWriteState *s=ms_new0(PulseWriteState,1);
s->rate=8000;
s->channels=1;
f->data=s;
check_pulse_ready();
}
static void pulse_write_preprocess(MSFilter *f){
PulseWriteState *s=(PulseWriteState*)f->data;
int err;
pa_sample_spec pss;
pa_buffer_attr attr;
if (context==NULL) return;
pss.format=PA_SAMPLE_S16LE;
pss.channels=s->channels;
pss.rate=s->rate;
attr.maxlength=-1;
attr.tlength=-1;
attr.prebuf=-1;
attr.minreq=-1;
attr.fragsize=s->fragsize=latency_req*s->channels*s->rate*2;
s->stream=pa_stream_new(context,"mediastreamer2",&pss,NULL);
if (s->stream==NULL){
ms_error("pa_stream_new() failed: %s",pa_strerror(pa_context_errno(context)));
return;
}
err=pa_stream_connect_playback(s->stream,NULL,&attr, PA_STREAM_ADJUST_LATENCY,NULL,NULL);
if (err!=0){
ms_error("pa_stream_connect_playback() failed");
}
}
static void pulse_write_process(MSFilter *f){
PulseWriteState *s=(PulseWriteState*)f->data;
mblk_t *im;
while((im=ms_queue_get(f->inputs[0]))!=NULL){
int bsize=msgdsize(im);
if (s->stream){
if (pa_stream_writable_size(s->stream)>=bsize){
//ms_message("Pushing data to pulseaudio");
pa_stream_write(s->stream,im->b_rptr,bsize,NULL,0,PA_SEEK_RELATIVE);
}
}
freemsg(im);
}
}
static void pulse_write_postprocess(MSFilter *f){
PulseWriteState *s=(PulseWriteState*)f->data;
if (s->stream) {
pa_stream_disconnect(s->stream);
pa_stream_unref(s->stream);
}
}
static MSFilterDesc pulse_write_desc={
.id=MS_PULSE_WRITE_ID,
.name="MSPulseWrite",
.text="Sound output plugin based on PulseAudio",
.ninputs=1,
.noutputs=0,
.category=MS_FILTER_OTHER,
.init=pulse_write_init,
.preprocess=pulse_write_preprocess,
.process=pulse_write_process,
.postprocess=pulse_write_postprocess,
.methods=pulse_read_methods
};
static MSFilter *pulse_card_create_reader(MSSndCard *card)
{
return ms_filter_new_from_desc (&pulse_read_desc);
}
static MSFilter *pulse_card_create_writer(MSSndCard *card)
{
return ms_filter_new_from_desc (&pulse_write_desc);
}
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