Estudiamos el motor VoIP Mediastreamer2. Parte 10

Material del artículo tomado de mi canal zen .



En el último artículo, hicimos un intercomunicador dúplex que intercambia una señal de audio a través de una sesión RTP dúplex. En este artículo, aprenderemos cómo escribir filtros y agregar un filtro de bricolaje a un intercomunicador de bricolaje.


Estamos desarrollando un complemento



, , .


, 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...". .


En este artículo, aprendimos a escribir filtros. Como habrás notado, la función nash_filter_process () manipula bloques de datos. Como el ejemplo es un ejemplo de capacitación, hubo un mínimo de capacidades de transmisión de medios para manipular bloques de datos.


En el próximo artículo, analizaremos las funciones de gestión de mensajes y colas de mensajes. Lo que en el futuro ayudará a desarrollar filtros con un procesamiento de información más complejo.


All Articles