We study the Mediastreamer2 VoIP engine. Part 10

Article material taken from my zen channel .



In the last article, we made a duplex intercom that exchanges an audio signal through a duplex RTP session. In this article, we will learn how to write filters and add a do-it-yourself filter to a do-it-yourself intercom.


We are developing a 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...". .


In this article, we learned how to write filters. As you may have noticed, the nash_filter_process () function manipulates data blocks. Since the example is a training example, there was involved a minimum of media streamer capabilities for manipulating data blocks.


In the next article, we will look at message queuing and message management features. Which in the future will help to develop filters with more complex information processing.


All Articles