mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-03-11 15:03:56 +00:00
substantial work towards playing audio on IDEOS/G1 type MSM chipset
android phones directly from C to dramatically reduce latency.
This commit is contained in:
parent
d96d8ff8d6
commit
8d2792ce74
12
Android.mk
12
Android.mk
@ -67,7 +67,10 @@ LOCAL_SRC_FILES:= \
|
||||
serval-dna/vomp.c \
|
||||
serval-dna/lsif.c \
|
||||
serval-dna/monitor.c \
|
||||
serval-dna/monitor-cli.c
|
||||
serval-dna/monitor-cli.c \
|
||||
serval-dna/codecs.c \
|
||||
serval-dna/audiodevices.c \
|
||||
serval-dna/audio_msm_g1.c
|
||||
|
||||
LOCAL_MODULE:= serval
|
||||
|
||||
@ -95,3 +98,10 @@ LOCAL_MODULE:= servald
|
||||
LOCAL_SRC_FILES:= serval-dna/servalwrap.c
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE:= playwav
|
||||
LOCAL_SRC_FILES:= serval-dna/playwav.c
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
@ -40,7 +40,10 @@ SRCS= batman.c \
|
||||
vomp.c \
|
||||
lsif.c \
|
||||
monitor.c \
|
||||
monitor-cli.c
|
||||
monitor-cli.c \
|
||||
codecs.c \
|
||||
audiodevices.c \
|
||||
audio_msm_g1.c
|
||||
|
||||
HAVE_VOIPTEST= @HAVE_VOIPTEST@
|
||||
ifeq ($(HAVE_VOIPTEST), 1)
|
||||
|
396
audio_msm_g1.c
Normal file
396
audio_msm_g1.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
Copyright (C) 2012 Paul Gardner-Stephen
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
Contains code derived from playwav2.c, which has the following notice:
|
||||
|
||||
Copyright (C) 2008 The Android Open Source Project
|
||||
*/
|
||||
|
||||
int setReason(char *fmt, ...);
|
||||
#define WHY(X) setReason("%s:%d:%s() %s",__FILE__,__LINE__,__FUNCTION__,X)
|
||||
#define WHYRETNULL(X) { setReason("%s:%d:%s() %s",__FILE__,__LINE__,__FUNCTION__,X); return NULL; }
|
||||
#define WHYF(F, ...) setReason("%s:%d:%s() " F, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__)
|
||||
#define WHY_perror(X) setReason("%s:%d:%s() %s: %s [errno=%d]", __FILE__, __LINE__, __FUNCTION__, X, strerror(errno), errno)
|
||||
|
||||
extern int playFd;
|
||||
extern int recordFd;
|
||||
extern int playBufferSize;
|
||||
extern int recordBufferSize;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#if 0
|
||||
#include <linux/msm_audio.h>
|
||||
#else
|
||||
/* ---------- linux/msm_audio.h -------- */
|
||||
|
||||
#define AUDIO_IOCTL_MAGIC 'a'
|
||||
|
||||
#define AUDIO_START _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned)
|
||||
#define AUDIO_STOP _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned)
|
||||
#define AUDIO_FLUSH _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned)
|
||||
#define AUDIO_GET_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 3, unsigned)
|
||||
#define AUDIO_SET_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 4, unsigned)
|
||||
#define AUDIO_GET_STATS _IOR(AUDIO_IOCTL_MAGIC, 5, unsigned)
|
||||
#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned)
|
||||
#define AUDIO_SET_ADRC _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned)
|
||||
#define AUDIO_SET_EQ _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned)
|
||||
#define AUDIO_SET_RX_IIR _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned)
|
||||
|
||||
#define EQ_MAX_BAND_NUM 12
|
||||
|
||||
#define ADRC_ENABLE 0x0001
|
||||
#define ADRC_DISABLE 0x0000
|
||||
#define EQ_ENABLE 0x0002
|
||||
#define EQ_DISABLE 0x0000
|
||||
#define IIR_ENABLE 0x0004
|
||||
#define IIR_DISABLE 0x0000
|
||||
|
||||
struct eq_filter_type
|
||||
{
|
||||
int16_t gain;
|
||||
uint16_t freq;
|
||||
uint16_t type;
|
||||
uint16_t qf;
|
||||
};
|
||||
|
||||
struct eqalizer
|
||||
{
|
||||
uint16_t bands;
|
||||
uint16_t params[132];
|
||||
};
|
||||
|
||||
struct rx_iir_filter
|
||||
{
|
||||
uint16_t num_bands;
|
||||
uint16_t iir_params[48];
|
||||
};
|
||||
|
||||
|
||||
struct msm_audio_config
|
||||
{
|
||||
uint32_t buffer_size;
|
||||
uint32_t buffer_count;
|
||||
uint32_t channel_count;
|
||||
uint32_t sample_rate;
|
||||
uint32_t codec_type;
|
||||
uint32_t unused[3];
|
||||
};
|
||||
|
||||
struct msm_audio_stats
|
||||
{
|
||||
uint32_t out_bytes;
|
||||
uint32_t unused[3];
|
||||
};
|
||||
|
||||
/* Audio routing */
|
||||
|
||||
#define SND_IOCTL_MAGIC 's'
|
||||
|
||||
#define SND_MUTE_UNMUTED 0
|
||||
#define SND_MUTE_MUTED 1
|
||||
|
||||
struct msm_snd_device_config
|
||||
{
|
||||
uint32_t device;
|
||||
uint32_t ear_mute;
|
||||
uint32_t mic_mute;
|
||||
};
|
||||
|
||||
#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *)
|
||||
|
||||
#define SND_METHOD_VOICE 0
|
||||
|
||||
#define SND_METHOD_VOICE_1 1
|
||||
|
||||
struct msm_snd_volume_config
|
||||
{
|
||||
uint32_t device;
|
||||
uint32_t method;
|
||||
uint32_t volume;
|
||||
};
|
||||
|
||||
#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *)
|
||||
|
||||
/* Returns the number of SND endpoints supported. */
|
||||
|
||||
#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned *)
|
||||
|
||||
struct msm_snd_endpoint
|
||||
{
|
||||
int id; /* input and output */
|
||||
char name[64]; /* output only */
|
||||
};
|
||||
|
||||
/* Takes an index between 0 and one less than the number returned by
|
||||
* SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a
|
||||
* SND endpoint. On input, the .id field contains the number of the
|
||||
* endpoint, and on exit it contains the SND index, while .name contains
|
||||
* the description of the endpoint.
|
||||
*/
|
||||
|
||||
#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *)
|
||||
|
||||
#endif
|
||||
|
||||
static int
|
||||
do_route_audio_rpc (uint32_t device, int ear_mute, int mic_mute)
|
||||
{
|
||||
if (device == -1UL)
|
||||
return 0;
|
||||
|
||||
int fd;
|
||||
|
||||
printf ("rpc_snd_set_device(%d, %d, %d)\n", device, ear_mute, mic_mute);
|
||||
|
||||
fd = open ("/dev/msm_snd", O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror ("Can not open snd device");
|
||||
return -1;
|
||||
}
|
||||
// RPC call to switch audio path
|
||||
/* rpc_snd_set_device(
|
||||
* device, # Hardware device enum to use
|
||||
* ear_mute, # Set mute for outgoing voice audio
|
||||
* # this should only be unmuted when in-call
|
||||
* mic_mute, # Set mute for incoming voice audio
|
||||
* # this should only be unmuted when in-call or
|
||||
* # recording.
|
||||
* )
|
||||
*/
|
||||
struct msm_snd_device_config args;
|
||||
args.device = device;
|
||||
args.ear_mute = ear_mute ? SND_MUTE_MUTED : SND_MUTE_UNMUTED;
|
||||
args.mic_mute = mic_mute ? SND_MUTE_MUTED : SND_MUTE_UNMUTED;
|
||||
|
||||
if (ioctl (fd, SND_SET_DEVICE, &args) < 0)
|
||||
{
|
||||
perror ("snd_set_device error.");
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close (fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
set_volume_rpc (uint32_t device, uint32_t method, uint32_t volume)
|
||||
{
|
||||
int fd;
|
||||
|
||||
printf ("rpc_snd_set_volume(%d, %d, %d)\n", device, method, volume);
|
||||
|
||||
if (device == -1UL)
|
||||
return 0;
|
||||
|
||||
fd = open ("/dev/msm_snd", O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
perror ("Can not open snd device");
|
||||
return -1;
|
||||
}
|
||||
/* rpc_snd_set_volume(
|
||||
* device, # Any hardware device enum, including
|
||||
* # SND_DEVICE_CURRENT
|
||||
* method, # must be SND_METHOD_VOICE to do anything useful
|
||||
* volume, # integer volume level, in range [0,5].
|
||||
* # note that 0 is audible (not quite muted)
|
||||
* )
|
||||
* rpc_snd_set_volume only works for in-call sound volume.
|
||||
*/
|
||||
struct msm_snd_volume_config args;
|
||||
args.device = device;
|
||||
args.method = method;
|
||||
args.volume = volume;
|
||||
|
||||
if (ioctl (fd, SND_SET_VOLUME, &args) < 0)
|
||||
{
|
||||
perror ("snd_set_volume error.");
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
close (fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* See if we can query end-points for this device.
|
||||
If so, assume we have detected it.
|
||||
*/
|
||||
char *audio_msm_g1_detect()
|
||||
{
|
||||
int fd = open ("/dev/msm_snd", O_RDWR);
|
||||
if (fd<0) return NULL;
|
||||
int endpoints=0;
|
||||
int rc =ioctl(fd,SND_GET_NUM_ENDPOINTS,&endpoints);
|
||||
close(fd);
|
||||
if (rc>0) return "G1/IDEOS style MSM audio";
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
/* Prepare audio path, volume etc, and then open play and
|
||||
record file descriptors.
|
||||
*/
|
||||
int audio_msm_g1_start_play()
|
||||
{
|
||||
/* Get audio control device */
|
||||
int fd = open ("/dev/msm_snd", O_RDWR);
|
||||
if (fd<0) return -1;
|
||||
|
||||
/* Look through endpoints for the regular in-call endpoint */
|
||||
int endpoints=0;
|
||||
int rc =ioctl(fd,SND_GET_NUM_ENDPOINTS,&endpoints);
|
||||
int endpoint=-1;
|
||||
int i;
|
||||
for(i=0;i<endpoints;i++) {
|
||||
struct msm_snd_endpoint ep;
|
||||
ep.id=i;
|
||||
ep.name[0]=0;
|
||||
ioctl(fd,SND_GET_ENDPOINT,&ep);
|
||||
if (!strcasecmp(ep.name,"HANDSET"))
|
||||
/* should this be i, or ep.id ? */
|
||||
endpoint=i;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
/* Set the specified endpoint and unmute microphone and speaker */
|
||||
do_route_audio_rpc(endpoint,SND_MUTE_UNMUTED,SND_MUTE_UNMUTED);
|
||||
|
||||
/* Set the volume (somewhat arbitrarily for now) */
|
||||
int vol=5;
|
||||
int dev=0xd; /* no one seems to know what this magic value means */
|
||||
set_volume_rpc(dev,SND_METHOD_VOICE_1, vol);
|
||||
|
||||
playFd=open("/dev/msm_pcm_out",O_RDWR);
|
||||
struct msm_audio_config config;
|
||||
if (ioctl(playFd, AUDIO_GET_CONFIG,&config))
|
||||
{
|
||||
close(playFd);
|
||||
return WHY("Could not read audio device configuration");
|
||||
}
|
||||
config.channel_count=1;
|
||||
config.sample_rate=8000;
|
||||
playBufferSize=config.buffer_size;
|
||||
if (ioctl(playFd, AUDIO_SET_CONFIG,&config))
|
||||
{
|
||||
close(playFd);
|
||||
return WHY("Could not set audio device configuration");
|
||||
}
|
||||
|
||||
/* tell hardware to start playing */
|
||||
ioctl(playFd,AUDIO_START,0);
|
||||
|
||||
WHY("G1/IDEOS style MSM audio device initialised and ready to play");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_msm_g1_stop_play()
|
||||
{
|
||||
if (playFd>-1) close(playFd);
|
||||
playFd=-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_msm_g1_start_record()
|
||||
{
|
||||
recordFd=open("/dev/msm_pcm_out",O_RDWR);
|
||||
struct msm_audio_config config;
|
||||
if (ioctl(recordFd, AUDIO_GET_CONFIG,&config))
|
||||
{
|
||||
close(recordFd);
|
||||
return WHY("Could not read audio device configuration");
|
||||
}
|
||||
config.channel_count=1;
|
||||
config.sample_rate=8000;
|
||||
if (ioctl(recordFd, AUDIO_SET_CONFIG,&config))
|
||||
{
|
||||
close(recordFd);
|
||||
return WHY("Could not set audio device configuration");
|
||||
}
|
||||
|
||||
/*
|
||||
If recordBufferSize equates to too long an interval,
|
||||
then try to reduce it in various ways.
|
||||
*/
|
||||
recordBufferSize=config.buffer_size;
|
||||
float bufferTime=recordBufferSize/2*1.0/config.sample_rate;
|
||||
if (bufferTime>0.02) {
|
||||
WHYF("REC buf=%.3fsecs, which is too long. Trying to reduce it.");
|
||||
|
||||
/* 64 bytes = 32 samples = ~4ms */
|
||||
config.buffer_size=64;
|
||||
if (!ioctl(recordFd, AUDIO_SET_CONFIG,&config))
|
||||
{
|
||||
recordBufferSize=config.buffer_size;
|
||||
bufferTime=recordBufferSize/2*1.0/config.sample_rate;
|
||||
WHYF("Succeeded in reducing record buffer to %d bytes (%.3fsecs)",
|
||||
recordBufferSize,bufferTime);
|
||||
goto fixedBufferSize;
|
||||
}
|
||||
recordBufferSize=64;
|
||||
|
||||
#if 0
|
||||
/* Ask for 2x speed and 2x channels, to divide effective buffer size by 4.
|
||||
*/
|
||||
config.sample_rate=16000;
|
||||
config.channel_count=2;
|
||||
#endif
|
||||
}
|
||||
fixedBufferSize:
|
||||
|
||||
/* tell hardware to start playing */
|
||||
ioctl(recordFd,AUDIO_START,0);
|
||||
|
||||
WHY("G1/IDEOS style MSM audio device initialised and ready to record");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_msm_g1_stop_record()
|
||||
{
|
||||
if (recordFd>-1) close(recordFd);
|
||||
recordFd=-1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int audio_msm_g1_stop()
|
||||
{
|
||||
audio_msm_g1_stop_play();
|
||||
audio_msm_g1_stop_record();
|
||||
}
|
||||
|
||||
int audio_msm_g1_start()
|
||||
{
|
||||
if (audio_msm_g1_start_play()) return -1;
|
||||
if (audio_msm_g1_start_record()) {
|
||||
audio_msm_g1_stop_play();
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
143
audiodevices.c
Normal file
143
audiodevices.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright (C) 2012 Paul Gardner-Stephen
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "serval.h"
|
||||
|
||||
#define AUDIO_MSM_G1_ETC 1
|
||||
#define AUDIO_MSM_N1_ETC 2
|
||||
int detectedAudioDevice=-1;
|
||||
char *detectedAudioDeviceName=NULL;
|
||||
|
||||
int playFd=-1;
|
||||
int recordFd=-1;
|
||||
int playBufferSize=0;
|
||||
int recordBufferSize=0;
|
||||
|
||||
int detectAudioDevice()
|
||||
{
|
||||
detectedAudioDeviceName=audio_msm_g1_detect();
|
||||
if (detectedAudioDeviceName) {
|
||||
detectedAudioDevice=AUDIO_MSM_G1_ETC;
|
||||
WHYF("Detected audio device '%s'",detectedAudioDeviceName);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int getAudioPlayFd()
|
||||
{
|
||||
return playFd;
|
||||
}
|
||||
|
||||
int getAudioRecordFd()
|
||||
{
|
||||
return recordFd;
|
||||
}
|
||||
|
||||
/* read some audio, but not more than bufferSize-offset bytes.
|
||||
*/
|
||||
int getAudioBytes(unsigned char *buffer,
|
||||
int offset,
|
||||
int bufferSize)
|
||||
{
|
||||
switch(detectedAudioDevice) {
|
||||
/* some devices require reading a whole buffer in one go */
|
||||
case AUDIO_MSM_G1_ETC:
|
||||
{
|
||||
if (bufferSize-offset<recordBufferSize) {
|
||||
return WHY("Supplied buffer has no space for new samples");
|
||||
}
|
||||
int b=read(recordFd,&buffer[offset],recordBufferSize);
|
||||
if (b>0) offset+=b;
|
||||
return offset;
|
||||
}
|
||||
break;
|
||||
/* while others allow reading an arbitrary amount */
|
||||
default:
|
||||
{
|
||||
int b=read(recordFd,&buffer[offset],bufferSize-offset);
|
||||
if (b>0) offset+=b;
|
||||
return offset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return WHYF("Reading audio for device class #%d not implemented",
|
||||
detectedAudioDevice);
|
||||
}
|
||||
|
||||
/* as with recording, some of the devices have a fixed buffer size that
|
||||
we must completely fill.
|
||||
*/
|
||||
int playBufferBytes=0;
|
||||
unsigned char playBuffer[65536];
|
||||
int playAudio(unsigned char *data,int bytes)
|
||||
{
|
||||
switch(detectedAudioDevice) {
|
||||
/* some devices require reading a whole buffer in one go */
|
||||
case AUDIO_MSM_G1_ETC:
|
||||
if (bytes+playBufferBytes>65536)
|
||||
return WHY("Play marshalling buffer full");
|
||||
bcopy(&data[0],&playBuffer[playBufferBytes],bytes);
|
||||
playBufferBytes+=bytes;
|
||||
int i;
|
||||
for(i=0;i<playBufferBytes;i+=playBufferSize)
|
||||
{
|
||||
if (write(playFd,&playBuffer[i],playBufferSize)<
|
||||
playBufferSize)
|
||||
break;
|
||||
}
|
||||
bcopy(&playBuffer[i],&playBuffer[0],playBufferBytes-i);
|
||||
playBufferBytes-=i;
|
||||
break;
|
||||
/* the rest we can just write() to */
|
||||
default:
|
||||
if (write(playFd,data,bytes)<bytes)
|
||||
return WHY("short write() when playing audio");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return WHYF("Playing audio for device class #%d not implemented",
|
||||
detectedAudioDevice);
|
||||
}
|
||||
|
||||
int stopAudio()
|
||||
{
|
||||
switch(detectedAudioDevice) {
|
||||
case AUDIO_MSM_G1_ETC:
|
||||
return audio_msm_g1_stop();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return WHYF("Stopping audio for device class #%d not implemented",
|
||||
detectedAudioDevice);
|
||||
}
|
||||
|
||||
int startAudio()
|
||||
{
|
||||
switch(detectedAudioDevice) {
|
||||
case AUDIO_MSM_G1_ETC:
|
||||
return audio_msm_g1_start();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return WHYF("Starting audio for device class #%d not implemented",
|
||||
detectedAudioDevice);
|
||||
}
|
42
codecs.c
Normal file
42
codecs.c
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (C) 2012 Paul Gardner-Stephen
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "serval.h"
|
||||
|
||||
int encodeAndDispatchRecordedAudio(int fd,int callSessionToken,
|
||||
int recordCodec,
|
||||
unsigned char *sampleData,
|
||||
int sampleBytes)
|
||||
{
|
||||
switch (recordCodec) {
|
||||
case VOMP_CODEC_PCM:
|
||||
/* leave data raw, so need to rewrite sampleData or sampleBytes */
|
||||
break;
|
||||
default:
|
||||
WHYF("Codec not yet supported");
|
||||
return -1;
|
||||
}
|
||||
|
||||
char msg[128+MAX_AUDIO_BYTES];
|
||||
snprintf(msg,128,"\n*%d:AUDIO:%x:%d\n",sampleBytes,callSessionToken,recordCodec);
|
||||
int len=strlen(msg);
|
||||
bcopy(&sampleData[0],&msg[len],sampleBytes);
|
||||
len+=sampleBytes;
|
||||
write(fd,msg,len);
|
||||
return 0;
|
||||
}
|
196
monitor-cli.c
196
monitor-cli.c
@ -1,3 +1,21 @@
|
||||
/*
|
||||
Copyright (C) 2012 Paul Gardner-Stephen
|
||||
|
||||
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
@ -28,6 +46,16 @@ int writeLine(char *msg)
|
||||
|
||||
int processChar(int c);
|
||||
|
||||
int autoAnswerP=1;
|
||||
int pipeAudio=1;
|
||||
int syntheticAudio=0;
|
||||
int showReceived=1;
|
||||
int interactiveP=1;
|
||||
int recordCodec=VOMP_CODEC_PCM;
|
||||
int recordCodecBlockSamples=320;
|
||||
int recordCodecTimespan=20;
|
||||
int callSessionToken=0;
|
||||
|
||||
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o)
|
||||
{
|
||||
const char *sid=NULL;
|
||||
@ -52,15 +80,26 @@ int app_monitor_cli(int argc, const char *const *argv, struct command_line_optio
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (pipeAudio) {
|
||||
detectAudioDevice();
|
||||
char *name=detectedAudioDeviceName;
|
||||
if (!name) {
|
||||
WHY("Could not detect any audio device. Will not pipe audio.");
|
||||
pipeAudio=0;
|
||||
}
|
||||
}
|
||||
|
||||
struct pollfd fds[128];
|
||||
int fdcount=0;
|
||||
|
||||
fds[fdcount].fd=fd;
|
||||
fds[fdcount].events=POLLIN;
|
||||
fdcount++;
|
||||
fds[fdcount].fd=STDIN_FILENO;
|
||||
fds[fdcount].events=POLLIN;
|
||||
fdcount++;
|
||||
if (interactiveP) {
|
||||
fds[fdcount].fd=STDIN_FILENO;
|
||||
fds[fdcount].events=POLLIN;
|
||||
fdcount++;
|
||||
}
|
||||
|
||||
writeLine("monitor vomp\n");
|
||||
writeLine("monitor rhizome\n");
|
||||
@ -71,25 +110,76 @@ int app_monitor_cli(int argc, const char *const *argv, struct command_line_optio
|
||||
writeLine(msg);
|
||||
}
|
||||
|
||||
char line[1024];
|
||||
/* Allow for up to one second of audio read from the microphone
|
||||
to be buffered. This is probably more than we will ever need.
|
||||
The primary purpose of the buffer is in fact to handle the fact
|
||||
that we are unlikely to ever read exaclty the number of samples
|
||||
we need, so we need to keep any left over ones from the previous
|
||||
read. */
|
||||
int audioRecordBufferBytes=0;
|
||||
unsigned char audioRecordBuffer[8000*2];
|
||||
|
||||
int base_fd_count=fdcount;
|
||||
while(1) {
|
||||
fdcount=base_fd_count;
|
||||
int audio_fd = getAudioRecordFd();
|
||||
if (audio_fd>-1) {
|
||||
fds[fdcount].fd=STDIN_FILENO;
|
||||
fds[fdcount].events=POLLIN;
|
||||
fdcount++;
|
||||
}
|
||||
poll(fds,fdcount,1000);
|
||||
|
||||
fcntl(fd,F_SETFL,
|
||||
fcntl(fd, F_GETFL, NULL)|O_NONBLOCK);
|
||||
fcntl(STDIN_FILENO,F_SETFL,
|
||||
fcntl(STDIN_FILENO, F_GETFL, NULL)|O_NONBLOCK);
|
||||
if (interactiveP)
|
||||
fcntl(STDIN_FILENO,F_SETFL,
|
||||
fcntl(STDIN_FILENO, F_GETFL, NULL)|O_NONBLOCK);
|
||||
if (audio_fd>-1)
|
||||
fcntl(audio_fd,F_SETFL,
|
||||
fcntl(audio_fd, F_GETFL, NULL)|O_NONBLOCK);
|
||||
|
||||
char line[1024];
|
||||
int bytes;
|
||||
int i;
|
||||
line[0]=0;
|
||||
bytes=read(fd,line,1024);
|
||||
if (bytes>0)
|
||||
for(i=0;i<bytes;i++) processChar(line[i]);
|
||||
bytes=read(STDIN_FILENO,line,1024);
|
||||
if (bytes>0) {
|
||||
line[bytes]=0;
|
||||
printf("< %s",line);
|
||||
write(fd,line,bytes);
|
||||
if (interactiveP) {
|
||||
bytes=read(STDIN_FILENO,line,1024);
|
||||
if (bytes>0) {
|
||||
line[bytes]=0;
|
||||
printf("< %s",line);
|
||||
write(fd,line,bytes);
|
||||
}
|
||||
if (audio_fd>-1) {
|
||||
/* attempt to read next audio buffer.
|
||||
Some audio devices that we are using require an entire buffer
|
||||
to be read at a time. */
|
||||
audioRecordBufferBytes
|
||||
=getAudioBytes(audioRecordBuffer,audioRecordBufferBytes,sizeof(audioRecordBuffer));
|
||||
/* 8KHz 16 bit samples = 16000 bytes per second.
|
||||
Thus one 1ms of audio = 16 bytes. */
|
||||
int audioRecordBufferOffset=0;
|
||||
while ((audioRecordBufferBytes-audioRecordBufferOffset)
|
||||
>recordCodecTimespan*16) {
|
||||
/* encode and deliver audio block to servald via monitor interface */
|
||||
encodeAndDispatchRecordedAudio(fd,callSessionToken,recordCodec,
|
||||
&audioRecordBuffer[audioRecordBufferOffset],
|
||||
recordCodecTimespan*16);
|
||||
/* skip over the samples we have already processed */
|
||||
audioRecordBufferOffset+=recordCodecTimespan*16;
|
||||
}
|
||||
/* copy the remaining buffered bytes down and correct buffer length */
|
||||
if (audioRecordBufferOffset<0) audioRecordBufferOffset=0;
|
||||
if (audioRecordBufferOffset>audioRecordBufferBytes)
|
||||
audioRecordBufferOffset=audioRecordBufferBytes;
|
||||
bcopy(&audioRecordBuffer[audioRecordBufferOffset],
|
||||
&audioRecordBuffer[0],
|
||||
audioRecordBufferBytes-audioRecordBufferOffset);
|
||||
audioRecordBufferBytes-=audioRecordBufferOffset;
|
||||
}
|
||||
}
|
||||
|
||||
fcntl(fd,F_SETFL,
|
||||
@ -106,52 +196,70 @@ int counter=0;
|
||||
int callState=0;
|
||||
int processLine(char *cmd,unsigned char *data,int dataLen)
|
||||
{
|
||||
int l_id,r_id,l_state,r_state;
|
||||
printf("> %s\n",cmd);
|
||||
if (data) {
|
||||
int i,j;
|
||||
for(i=0;i<dataLen;i+=16) {
|
||||
printf(" %04x :",i);
|
||||
for(j=0;j<16;j++)
|
||||
if (i+j<dataLen) printf(" %02x",data[i+j]); else printf(" ");
|
||||
printf(" ");
|
||||
for(j=0;j<16;j++)
|
||||
if (i+j<dataLen) {
|
||||
if (data[i+j]>=0x20&&data[i+j]<0x7e)
|
||||
printf("%c",data[i+j]); else printf(".");
|
||||
}
|
||||
printf("\n");
|
||||
int l_id,r_id,l_state,r_state,codec;
|
||||
long long start_time,end_time;
|
||||
if (showReceived) {
|
||||
printf("> %s\n",cmd);
|
||||
if (data) {
|
||||
int i,j;
|
||||
for(i=0;i<dataLen;i+=16) {
|
||||
printf(" %04x :",i);
|
||||
for(j=0;j<16;j++)
|
||||
if (i+j<dataLen) printf(" %02x",data[i+j]); else printf(" ");
|
||||
printf(" ");
|
||||
for(j=0;j<16;j++)
|
||||
if (i+j<dataLen) {
|
||||
if (data[i+j]>=0x20&&data[i+j]<0x7e)
|
||||
printf("%c",data[i+j]); else printf(".");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (sscanf(cmd,"AUDIOPACKET:%x:%x:%d:%d:%d:%lld:%lld",
|
||||
&l_id,&r_id,&l_state,&r_state,
|
||||
&codec,&start_time,&end_time)==7)
|
||||
{
|
||||
if (pipeAudio) {
|
||||
playAudio(data,dataLen);
|
||||
}
|
||||
}
|
||||
if (sscanf(cmd,"CALLSTATUS:%x:%x:%d:%d",
|
||||
&l_id,&r_id,&l_state,&r_state)==4)
|
||||
{
|
||||
if (l_state==4) {
|
||||
if (l_state==4&&autoAnswerP) {
|
||||
// We are ringing, so pickup
|
||||
char msg[1024];
|
||||
sprintf(msg,"pickup %x\n",l_id);
|
||||
writeLine(msg);
|
||||
}
|
||||
if (l_state==5) {
|
||||
startAudio();
|
||||
callSessionToken=l_id;
|
||||
} else {
|
||||
stopAudio();
|
||||
callSessionToken=0;
|
||||
}
|
||||
callState=l_state;
|
||||
}
|
||||
if (sscanf(cmd,"KEEPALIVE:%x",&l_id)==1) {
|
||||
if (callState==5) {
|
||||
/* Send synthetic audio packet */
|
||||
char buffer[1024];
|
||||
sprintf(buffer,"*320:AUDIO:%x:8\n"
|
||||
"%08d pasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456",l_id,counter++);
|
||||
writeLine(buffer);
|
||||
printf("< *320:AUDIO:%x:8\\n<320 bytes>\n",l_id);
|
||||
}
|
||||
if (callState==5&&syntheticAudio) {
|
||||
/* Send synthetic audio packet */
|
||||
char buffer[1024];
|
||||
sprintf(buffer,"*320:AUDIO:%x:8\n"
|
||||
"%08d pasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456"
|
||||
"qwertyuiopasdfghjklzxcvbnm123456",l_id,counter++);
|
||||
writeLine(buffer);
|
||||
printf("< *320:AUDIO:%x:8\\n<320 bytes>\n",l_id);
|
||||
}
|
||||
}
|
||||
cmd[0]=0;
|
||||
cmdLen=0;
|
||||
|
22
serval.h
22
serval.h
@ -1407,3 +1407,25 @@ int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax);
|
||||
int monitor_call_status(vomp_call_state *call);
|
||||
int monitor_send_audio(vomp_call_state *call,overlay_mdp_frame *audio);
|
||||
extern int monitor_socket_count;
|
||||
|
||||
#define AUDIO_MSM_G1_ETC 1
|
||||
#define AUDIO_MSM_N1_ETC 2
|
||||
extern int detectedAudioDevice;
|
||||
extern char *detectedAudioDeviceName;
|
||||
int detectAudioDevice();
|
||||
int getAudioPlayFd();
|
||||
int getAudioRecordFd();
|
||||
int getAudioFd();
|
||||
int getAudioBytes(unsigned char *buffer,
|
||||
int offset,
|
||||
int bufferSize);
|
||||
int playAudio(unsigned char *data,int bytes);
|
||||
int stopAudio();
|
||||
int startAudio();
|
||||
int encodeAndDispatchRecordedAudio(int fd,int callSessionToken,
|
||||
int recordCodec,
|
||||
unsigned char *sampleData,
|
||||
int sampleBytes);
|
||||
char *audio_msm_g1_detect();
|
||||
int audio_msm_g1_start();
|
||||
int audio_msm_g1_stop();
|
||||
|
Loading…
x
Reference in New Issue
Block a user