mirror of
https://gitlab.linphone.org/BC/public/linphone-iphone.git
synced 2026-05-07 05:53:06 +00:00
git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@208 3f6dc0c8-ddfe-455d-9043-3cd528dc4637
304 lines
6.9 KiB
C
304 lines
6.9 KiB
C
/*
|
|
mediastreamer2 library - modular sound and video processing and streaming
|
|
Copyright (C) 2006 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/msfileplayer.h"
|
|
#include "mediastreamer2/waveheader.h"
|
|
#include "mediastreamer2/msticker.h"
|
|
|
|
|
|
static int player_close(MSFilter *f, void *arg);
|
|
|
|
typedef enum {
|
|
CLOSED,
|
|
STARTED,
|
|
STOPPED
|
|
} PlayerState;
|
|
|
|
struct _PlayerData{
|
|
int fd;
|
|
PlayerState state;
|
|
int rate;
|
|
int nchannels;
|
|
int hsize;
|
|
int loop_after;
|
|
int pause_time;
|
|
bool_t swap;
|
|
};
|
|
|
|
typedef struct _PlayerData PlayerData;
|
|
|
|
static void player_init(MSFilter *f){
|
|
PlayerData *d=ms_new(PlayerData,1);
|
|
d->fd=-1;
|
|
d->state=CLOSED;
|
|
d->swap=FALSE;
|
|
d->rate=8000;
|
|
d->nchannels=1;
|
|
d->hsize=0;
|
|
d->loop_after=-1; /*by default, don't loop*/
|
|
d->pause_time=0;
|
|
f->data=d;
|
|
}
|
|
|
|
static int read_wav_header(PlayerData *d){
|
|
|
|
char header1[sizeof(riff_t)];
|
|
char header2[sizeof(format_t)];
|
|
char header3[sizeof(data_t)];
|
|
int count;
|
|
|
|
riff_t *riff_chunk=(riff_t*)header1;
|
|
format_t *format_chunk=(format_t*)header2;
|
|
data_t *data_chunk=(data_t*)header3;
|
|
|
|
unsigned long len=0;
|
|
|
|
len = read(d->fd, header1, sizeof(header1)) ;
|
|
if (len != sizeof(header1)){
|
|
ms_warning("Wrong wav header: cannot read file");
|
|
return -1;
|
|
}
|
|
|
|
if (0!=strncmp(riff_chunk->riff, "RIFF", 4) || 0!=strncmp(riff_chunk->wave, "WAVE", 4)){
|
|
ms_warning("Wrong wav header (not RIFF/WAV)");
|
|
return -1;
|
|
}
|
|
|
|
len = read(d->fd, header2, sizeof(header2)) ;
|
|
if (len != sizeof(header2)){
|
|
ms_warning("Wrong wav header: cannot read file");
|
|
return -1;
|
|
}
|
|
|
|
d->rate=le_uint32(format_chunk->rate);
|
|
d->nchannels=le_uint16(format_chunk->channel);
|
|
|
|
if (format_chunk->len-0x10>0)
|
|
{
|
|
lseek(d->fd,(format_chunk->len-0x10),SEEK_CUR);
|
|
}
|
|
|
|
d->hsize=sizeof(wave_header_t)-0x10+format_chunk->len;
|
|
|
|
len = read(d->fd, header3, sizeof(header3)) ;
|
|
if (len != sizeof(header3)){
|
|
ms_warning("Wrong wav header: cannot read file");
|
|
return -1;
|
|
}
|
|
count=0;
|
|
while (strncmp(data_chunk->data, "data", 4)!=0 && count<30)
|
|
{
|
|
ms_warning("skipping chunk=%s len=%i", data_chunk->data, data_chunk->len);
|
|
lseek(d->fd,data_chunk->len,SEEK_CUR);
|
|
count++;
|
|
d->hsize=d->hsize+len+data_chunk->len;
|
|
|
|
len = read(d->fd, header3, sizeof(header3)) ;
|
|
if (len != sizeof(header3)){
|
|
ms_warning("Wrong wav header: cannot read file");
|
|
return -1;
|
|
}
|
|
}
|
|
#ifdef WORDS_BIGENDIAN
|
|
if (le_uint16(format_chunk->blockalign)==le_uint16(format_chunk->channel) * 2)
|
|
d->swap=TRUE;
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int player_open(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
int fd;
|
|
const char *file=(const char*)arg;
|
|
|
|
if (d->fd>=0){
|
|
player_close(f,NULL);
|
|
}
|
|
if ((fd=open(file,O_RDONLY))==-1){
|
|
ms_warning("Failed to open %s",file);
|
|
return -1;
|
|
}
|
|
d->state=STOPPED;
|
|
d->fd=fd;
|
|
if (strstr(file,".wav")!=NULL) read_wav_header(d);
|
|
ms_message("%s opened: rate=%i,channel=%i",file,d->rate,d->nchannels);
|
|
return 0;
|
|
}
|
|
|
|
static int player_start(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
if (d->state==STOPPED)
|
|
d->state=STARTED;
|
|
return 0;
|
|
}
|
|
|
|
static int player_stop(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
ms_filter_lock(f);
|
|
if (d->state==STARTED){
|
|
d->state=STOPPED;
|
|
lseek(d->fd,d->hsize,SEEK_SET);
|
|
}
|
|
ms_filter_unlock(f);
|
|
return 0;
|
|
}
|
|
|
|
static int player_close(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
player_stop(f,NULL);
|
|
if (d->fd>=0) close(d->fd);
|
|
d->fd=-1;
|
|
d->state=CLOSED;
|
|
return 0;
|
|
}
|
|
|
|
static void player_uninit(MSFilter *f){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
if (d->fd>=0) player_close(f,NULL);
|
|
ms_free(d);
|
|
}
|
|
|
|
static void swap_bytes(unsigned char *bytes, int len){
|
|
int i;
|
|
unsigned char tmp;
|
|
for(i=0;i<len;i+=2){
|
|
tmp=bytes[i];
|
|
bytes[i]=bytes[i+1];
|
|
bytes[i+1]=tmp;
|
|
}
|
|
}
|
|
|
|
static void player_process(MSFilter *f){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
int bytes=2*(f->ticker->interval*d->rate*d->nchannels)/1000;
|
|
ms_filter_lock(f);
|
|
if (d->state==STARTED){
|
|
int err;
|
|
mblk_t *om=allocb(bytes,0);
|
|
if (d->pause_time>0){
|
|
err=bytes;
|
|
memset(om->b_wptr,0,bytes);
|
|
d->pause_time-=f->ticker->interval;
|
|
}else{
|
|
err=read(d->fd,om->b_wptr,bytes);
|
|
if (d->swap) swap_bytes(om->b_wptr,bytes);
|
|
}
|
|
if (err>=0){
|
|
if (err!=0){
|
|
om->b_wptr+=bytes;
|
|
ms_queue_put(f->outputs[0],om);
|
|
}else freemsg(om);
|
|
if (err<bytes){
|
|
ms_filter_notify_no_arg(f,MS_FILE_PLAYER_EOF);
|
|
lseek(d->fd,d->hsize,SEEK_SET);
|
|
|
|
/* special value for playing file only once */
|
|
if (d->loop_after==-2)
|
|
{
|
|
d->state=STOPPED;
|
|
ms_filter_unlock(f);
|
|
return;
|
|
}
|
|
|
|
if (d->loop_after>=0){
|
|
d->pause_time=d->loop_after;
|
|
}
|
|
}
|
|
}else{
|
|
ms_warning("Fail to read %i bytes: %s",bytes,strerror(errno));
|
|
}
|
|
}
|
|
ms_filter_unlock(f);
|
|
}
|
|
|
|
static int player_get_sr(MSFilter *f, void*arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
*((int*)arg)=d->rate;
|
|
return 0;
|
|
}
|
|
|
|
static int player_loop(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
d->loop_after=*((int*)arg);
|
|
return 0;
|
|
}
|
|
|
|
static int player_eof(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
if (d->fd<0 && d->state==CLOSED)
|
|
*((int*)arg) = TRUE; /* 1 */
|
|
else
|
|
*((int*)arg) = FALSE; /* 0 */
|
|
return 0;
|
|
}
|
|
|
|
static int player_get_nch(MSFilter *f, void *arg){
|
|
PlayerData *d=(PlayerData*)f->data;
|
|
*((int*)arg)=d->nchannels;
|
|
return 0;
|
|
}
|
|
|
|
static MSFilterMethod player_methods[]={
|
|
{ MS_FILE_PLAYER_OPEN, player_open },
|
|
{ MS_FILE_PLAYER_START, player_start },
|
|
{ MS_FILE_PLAYER_STOP, player_stop },
|
|
{ MS_FILE_PLAYER_CLOSE, player_close },
|
|
{ MS_FILTER_GET_SAMPLE_RATE, player_get_sr},
|
|
{ MS_FILTER_GET_NCHANNELS, player_get_nch },
|
|
{ MS_FILE_PLAYER_LOOP, player_loop },
|
|
{ MS_FILE_PLAYER_DONE, player_eof },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
#ifdef WIN32
|
|
|
|
MSFilterDesc ms_file_player_desc={
|
|
MS_FILE_PLAYER_ID,
|
|
"MSFilePlayer",
|
|
N_("Raw files and wav reader"),
|
|
MS_FILTER_OTHER,
|
|
NULL,
|
|
0,
|
|
1,
|
|
player_init,
|
|
NULL,
|
|
player_process,
|
|
NULL,
|
|
player_uninit,
|
|
player_methods
|
|
};
|
|
|
|
#else
|
|
|
|
MSFilterDesc ms_file_player_desc={
|
|
.id=MS_FILE_PLAYER_ID,
|
|
.name="MSFilePlayer",
|
|
.text=N_("Raw files and wav reader"),
|
|
.category=MS_FILTER_OTHER,
|
|
.ninputs=0,
|
|
.noutputs=1,
|
|
.init=player_init,
|
|
.process=player_process,
|
|
.uninit=player_uninit,
|
|
.methods=player_methods
|
|
};
|
|
|
|
#endif
|
|
|
|
MS_FILTER_DESC_EXPORT(ms_file_player_desc)
|