Wir untersuchen die Mediastreamer2 VoIP-Engine. Teil 10

Artikelmaterial aus meinem Zen-Kanal .



Im letzten Artikel haben wir eine Duplex-Gegensprechanlage hergestellt, die ein Audiosignal über eine Duplex-RTP-Sitzung austauscht. In diesem Artikel erfahren Sie, wie Sie Filter schreiben und einer Do-it-yourself-Gegensprechanlage einen Do-it-yourself-Filter hinzufügen.


Wir entwickeln ein 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 diesem Artikel haben wir gelernt, wie man Filter schreibt. Wie Sie vielleicht bemerkt haben, bearbeitet die Funktion nash_filter_process () Datenblöcke. Da es sich bei dem Beispiel um ein Trainingsbeispiel handelt, war ein Minimum an Media-Streamer-Funktionen zum Bearbeiten von Datenblöcken erforderlich.


Im nächsten Artikel werden wir uns mit den Funktionen für Nachrichtenwarteschlange und Nachrichtenverwaltung befassen. Dies wird in Zukunft dazu beitragen, Filter mit komplexerer Informationsverarbeitung zu entwickeln.


All Articles