Nous étudions le moteur VoIP Mediastreamer2. Partie 10

Article extrait de ma chaîne zen .



Dans le dernier article, nous avons réalisé un interphone duplex qui échange un signal audio via une session RTP duplex. Dans cet article, nous allons apprendre à écrire des filtres et à ajouter un filtre à faire soi-même à un interphone à faire soi-même.


Nous développons un plugin



, , .


, include . , ms_filter_register() . , .


. , , . , , .


, _ (NASH_FILTR). — . , , .


. / .


. . msfilter.h, MS_FILTER_METHOD ( ), ( ) MSFilterDesc . :


struct _MSFilterDesc{
    MSFilterId id;    /*       allfilters.h   .  */
    const char *name; /*   ( ). */
    const char *text; /**  ,  . */
    MSFilterCategory category; /*  ,   . */
    const char *enc_fmt; /* sub-mime  ,        MS_FILTER_ENCODER  MS_FILTER_DECODER */
    int ninputs; /*  . */
    int noutputs; /* .  */
    MSFilterFunc init; /*    . */
    MSFilterFunc preprocess; /*        . */
    MSFilterFunc process; /**<     ,       MSTicker. */
    MSFilterFunc postprocess; /*    ,      process(),   . */
    MSFilterFunc uninit; /**<    ,   ,        . */
    MSFilterMethod *methods; /*   . */
    unsigned int flags; /*         MSFilterFlags. */
};

/*
    .
*/
typedef struct _MSFilterDesc MSFilterDesc;

, msfilter.h. :


/*  nash_filter.h,  -  . */

#ifndef myfilter_h
#define myfilter_h

/*       . */
#include <mediastreamer2/msticker.h>

/* 
        .     
         .       allfilters.h
      enum MSFilterId.  , 
       ,     
   .      id     
   : 4000.   ,     , 
       .  
   */
#define NASH_FILTER_ID 4000

/* 
      .    
     ,   0.      
   ,        .  
       ,   . 
   */
#define NASH_FILTER_SET_TRESHOLD MS_FILTER_METHOD(NASH_FILTER_ID , 0, float)
#define NASH_FILTER_TUNE_OFF     MS_FILTER_METHOD_NO_ARG(NASH_FILTER_ID ,1)
#define NASH_FILTER_TUNE_ON      MS_FILTER_METHOD_NO_ARG(NASH_FILTER_ID ,2)

/*   ,      . */
struct _NASHFilterEvent
{
    /*  ,     ,
       0 -  , 1 -  .*/
    char state; 
    /* ,   . */
    uint64_t time;
};
typedef struct _NASHFilterEvent NASHFilterEvent;

/*     . */
#define NASH_FILTER_EVENT MS_FILTER_EVENT(MS_RTP_RECV_ID, 0, NASHFilterEvent)

/*   ,  
        . */
extern MSFilterDesc nash_filter_desc;

#endif /* myfilter_h */

. . , . nash_filter_desc. " " .


/*  nash_filter.,  -  . */

#include "nash_filter.h"
#include <math.h>

#define NASH_FILTER_NOUTPUTS 5

/*  ,     . */
typedef struct _nash_filterData
{
    bool_t disable_out;  /*     . */
    int last_state;   /*   . */
    char zero_count;     /*   . */
    char lag;            /*      . */
    char n_count;        /*   . */
    float skz_level;     /*    
,      .   
,     .  */

} nash_filterData;

/*----------------------------------------------------------*/
/*   . */
static void nash_filter_init(MSFilter *f)
{
    nash_filterData *d=ms_new0(nash_filterData, 1);
    d->lag=5;
    f->data=d;
}

/*----------------------------------------------------------*/
/*     ,
    . */
static void nash_filter_uninit(MSFilter *f)
{
    ms_free(f->data);
}

/*----------------------------------------------------------*/
/*     , 
      . */
char zero_array[1024]={0};

/*   . */
NASHFilterEvent event;

/*----------------------------------------------------------*/
/*   . */
static void send_event(MSFilter *f, int state)
{
    nash_filterData *d =( nash_filterData* ) f->data;
     d->last_state = state;
    /*    ,
          .   . */
    event.time=f -> ticker -> time;
    event.state=state;  
    ms_filter_notify(f, NASH_FILTER_EVENT, &event);
}   

/*----------------------------------------------------------*/
/*    ()   
  . */
static float calc_skz(nash_filterData *d, int16_t *signal, int numsamples)
{
    int i;
    float acc = 0;
    for (i=0; i<numsamples; i++)
    {
        int s=signal[i];
        acc = acc + s * s;
    }
    float skz = (float)sqrt(acc / numsamples);
    return skz;
}

/*----------------------------------------------------------*/
/*     ,
      . */
static void nash_filter_process(MSFilter *f)
{
    nash_filterData *d=(nash_filterData*)f->data;

    /*       . */
    mblk_t *im;
    int i;
    int state;
    /*     
          . */
    while((im=ms_queue_get(f->inputs[0]))!=NULL)
    {
        /*   ,     . */
        if ( d -> disable_out)
        {
          freemsg(im);
          continue;
        }

        /*         . */
        float skz = calc_skz(d, (int16_t*)im->b_rptr, msgdsize(im));
        state = (skz > d->skz_level) ? 1 : 0; 
        if (state) 
        {
            d->n_count++;
            d->zero_count = 0;
        }
        else
        {
            d->n_count = 0;
            d->zero_count++;
        }
        if (((d->zero_count > d->lag) || (d->n_count > d->lag))
            &&  (d->last_state != state)) send_event(f, state);

        /*         . 
         *   ,    .  
         *      0,      
         * . */ 
        int output_count = 0;
        mblk_t *outm; /*       . */
        for(i=0; i < f->desc->noutputs; i++)
        {
            if (f->outputs[i]!=NULL)
            {
                if (output_count == 0)
                {
                    outm = im;
                }
                else
                {
                    /*    . */       
                    outm = dupmsg(im);
                }
                /*        
                 *  . */ 
                ms_queue_put(f->outputs[i], outm);
                output_count++;
            }
        }
    }
}

/*----------------------------------------------------------*/
/* -   NASH_FILTER_SET_LAG. */
static int nash_filter_set_treshold(MSFilter *f, void *arg)
{
    nash_filterData *d=(nash_filterData*)f->data;
    d->skz_level=*(float*)arg;
    return 0;
}

/*----------------------------------------------------------*/
/* -   NASH_FILTER_TUNE_OFF. */
static int nash_filter_tune_off(MSFilter *f, void *arg)
{
    nash_filterData *d=(nash_filterData*)f->data;
    d->disable_out=TRUE;
    return 0;
}

/*----------------------------------------------------------*/
/* -   NASH_FILTER_TUNE_ON. */
static int nash_filter_tune_on(MSFilter *f, void *arg)
{
    nash_filterData *d=(nash_filterData*)f->data;
    d->disable_out=FALSE;
    return 0;
}

/*----------------------------------------------------------*/
/*    ,  
         
   . */
static MSFilterMethod nash_filter_methods[]={
    { NASH_FILTER_SET_TRESHOLD, nash_filter_set_treshold },
    { NASH_FILTER_TUNE_OFF, nash_filter_tune_off },
    { NASH_FILTER_TUNE_ON, nash_filter_tune_on },
    { 0 , NULL } /*   . */
};

/*----------------------------------------------------------*/
/*    . */
MSFilterDesc nash_filter_desc=
{
    NASH_FILTER_ID,
    "NASH_FILTER",
    "A filter with noise gate that reads from input and copy to it's five outputs.",
    MS_FILTER_OTHER,
    NULL,
    1,
    NASH_FILTER_NOUTPUTS,
    nash_filter_init,
    NULL,
    nash_filter_process,
    NULL,
    nash_filter_uninit,
    nash_filter_methods,
    0
};

MS_FILTER_DESC_EXPORT(nash_filter_desc)

, , . .
- - . .


-, wav. , . .
. , . "0", . "1" .


: --ng, --rec, record.wav.


/*  mstest9.c    c  
 * . */

#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/dtmfgen.h>
#include <mediastreamer2/msrtp.h>
#include <mediastreamer2/msfilerec.h>

/*   . */
#include "nash_filter.h"

/*    . */
#include "mstest_common.c"

/*----------------------------------------------------------*/
struct _app_vars
{
    int  local_port;              /*  . */
    int  remote_port;             /*      . */
    char remote_addr[128];        /* IP-  . */
    MSDtmfGenCustomTone dtmf_cfg; /*    . */
    MSFilter* recorder;           /*    . */
    bool_t file_is_open;          /*  ,     . */
    /* ,        . */
    float treshold; 
    bool_t en_rec;                /*   .*/    
};

typedef struct _app_vars app_vars;

/*----------------------------------------------------------*/
/*   RTP-. */
RtpSession* create_duplex_rtp_session(app_vars v)
{
    RtpSession *session = create_rtpsession (v.local_port, v.local_port + 1,
            FALSE, RTP_SESSION_SENDRECV);
    rtp_session_set_remote_addr_and_port(session, v.remote_addr, v.remote_port,
            v.remote_port + 1);
    rtp_session_set_send_payload_type(session, PCMU);
    return session;
}

/*----------------------------------------------------------*/
/*       
 *  . */
void  scan_args(int argc, char *argv[], app_vars *v)
{
    char i;
    for (i=0; i<argc; i++)
    {
        if (!strcmp(argv[i], "--help"))
        {
            char *p=argv[0]; p=p + 2;
            printf("  %s walkie talkie\n\n", p);
            printf("--help      List of options.\n");
            printf("--version   Version of application.\n");
            printf("--addr      Remote abonent IP address string.\n");
            printf("--port      Remote abonent port number.\n");
            printf("--lport     Local port number.\n");
            printf("--gen       Generator frequency.\n");
            printf("--ng        Noise gate treshold level from 0. to 1.0\n");
            printf("--rec       record to file 'record.wav'.\n");
            exit(0);
        }

        if (!strcmp(argv[i], "--version"))
        {
            printf("0.1\n");
            exit(0);
        }

        if (!strcmp(argv[i], "--addr"))
        {
            strncpy(v->remote_addr, argv[i+1], 16);
            v->remote_addr[16]=0;
            printf("remote addr: %s\n", v->remote_addr);
        }

        if (!strcmp(argv[i], "--port"))
        {
            v->remote_port=atoi(argv[i+1]);
            printf("remote port: %i\n", v->remote_port);
        }

        if (!strcmp(argv[i], "--lport"))
        {
            v->local_port=atoi(argv[i+1]);
            printf("local port : %i\n", v->local_port);
        }

        if (!strcmp(argv[i], "--gen"))
        {
            v -> dtmf_cfg.frequencies[0] = atoi(argv[i+1]);
            printf("gen freq : %i\n", v -> dtmf_cfg.frequencies[0]);
        }

        if (!strcmp(argv[i], "--ng"))
        {
            v -> dtmf_cfg.frequencies[0] = atoi(argv[i+1]);
            printf("noise gate treshold: %f\n", v -> treshold);
        }
         if (!strcmp(argv[i], "--rec"))
        {
            v -> en_rec = TRUE;
            printf("enable recording: %i\n", v -> en_rec);
        }
    }
}

/*----------------------------------------------------------*/
/*   ,    ,   
 * ,        . */
static void change_detected_cb(void *data, MSFilter *f, unsigned int event_id,
        NASHFilterEvent *ev)
{
    app_vars *vars = (app_vars*) data;

    /*     ,  . */
    if (! vars -> en_rec) return; 

    if (ev -> state)
    {
        /*  . */
        if(!vars->file_is_open)
        {
            ms_filter_call_method(vars->recorder, MS_FILE_REC_OPEN, "record.wav");
            vars->file_is_open = 1;
        }
        ms_filter_call_method(vars->recorder, MS_FILE_REC_START, 0);
        printf("Recording...\n");
    }
    else
    {
        /*  . */
        ms_filter_call_method(vars->recorder, MS_FILE_REC_STOP, 0);
        printf("Pause...\n");
    }
}

/*----------------------------------------------------------*/
int main(int argc, char *argv[])
{
    /*    . */
    app_vars vars={5004, 7010, "127.0.0.1", {0}, 0, 0, 0.01, 0};

    /*      
     *     . */
    scan_args(argc, argv, &vars);

    ms_init();

    /*     . */
    MSSndCard *snd_card =
        ms_snd_card_manager_get_default_card(ms_snd_card_manager_get());
    MSFilter *snd_card_read = ms_snd_card_create_reader(snd_card);
    MSFilter *dtmfgen = ms_filter_new(MS_DTMF_GEN_ID);
    MSFilter *rtpsend = ms_filter_new(MS_RTP_SEND_ID);

    /*   . */
    MSFilter *encoder = ms_filter_create_encoder("PCMU");

    /*   . */
    register_payloads();

    /*   RTP-. */
    RtpSession* rtp_session = create_duplex_rtp_session(vars);
    ms_filter_call_method(rtpsend, MS_RTP_SEND_SET_SESSION, rtp_session);

    /*   . */
    ms_filter_link(snd_card_read, 0, dtmfgen, 0);
    ms_filter_link(dtmfgen, 0, encoder, 0);
    ms_filter_link(encoder, 0, rtpsend, 0);

    /*    . */
    MSFilter *rtprecv = ms_filter_new(MS_RTP_RECV_ID);
    ms_filter_call_method(rtprecv, MS_RTP_RECV_SET_SESSION, rtp_session);

    /*   . */
    MSFilter *decoder=ms_filter_create_decoder("PCMU");
    //MS_FILE_REC_ID

    /*   . */
    ms_filter_register(&nash_filter_desc);
    MSFilter *nash = ms_filter_new(NASH_FILTER_ID);

    /*    . */
    MSFilter *snd_card_write = ms_snd_card_create_writer(snd_card);

    /*   . */
    MSFilter *recorder=ms_filter_new(MS_FILE_REC_ID);
    vars.recorder = recorder; 

    /*    . */
    ms_filter_link(rtprecv, 0, decoder, 0);
    ms_filter_link(decoder, 0, nash, 0);
    ms_filter_link(nash, 0, snd_card_write, 0);
    ms_filter_link(nash, 1, recorder, 0);

    /*      ,    
     *        
     * ,        
     * . */
    ms_filter_set_notify_callback(nash,
            (MSFilterNotifyFunc)change_detected_cb, &vars);
    ms_filter_call_method(nash,NASH_FILTER_SET_TRESHOLD, &vars.treshold); 

    /*    - . */
    MSTicker *ticker = ms_ticker_new();

    /*   . */
    ms_ticker_attach(ticker, snd_card_read);
    ms_ticker_attach(ticker, rtprecv);

    /*       ,   . */   
    if (vars.dtmf_cfg.frequencies[0])
    {
        /*  ,    . */
        vars.dtmf_cfg.duration = 10000;
        vars.dtmf_cfg.amplitude = 1.0;
    }

    /*    . */
    printf("Press ENTER to exit.\n ");
    char c=getchar();
    while(c != '\n')
    {
        if(vars.dtmf_cfg.frequencies[0])
        {
            /*   . */
            ms_filter_call_method(dtmfgen, MS_DTMF_GEN_PLAY_CUSTOM,
                    (void*)&vars.dtmf_cfg);
        }
        char c=getchar();
        printf("--\n");
    }
    if (vars.en_rec ) ms_filter_call_method(recorder, MS_FILE_REC_CLOSE, 0);
}

- , math, , , :


$ gcc mstest9.c nash_filter.c -o mstest9   `pkg-config mediastreamer   --libs --cflags`  -lm

, :


$ ./mstest9  --lport 7010  --port 8010 --addr <   > --rec

:


$ ./mstest9  --lport 8010  --port 7010 --addr <   >

, . "Recording...". "Pause...". .


Dans cet article, nous avons appris à écrire des filtres. Comme vous l'avez peut-être remarqué, la fonction nash_filter_process () manipule les blocs de données. Étant donné que l'exemple est un exemple de formation, il y avait un minimum de capacités de streamer multimédia pour manipuler des blocs de données.


Dans le prochain article, nous verrons les fonctionnalités de mise en file d'attente et de gestion des messages. Ce qui à l'avenir aidera à développer des filtres avec un traitement de l'information plus complexe.


All Articles