PJSIPは通話中に相手の携帯電話から送られてきたin-bnad DTMF(no rfc 2833)ボタン情報を検出する

14104 ワード

最近プロジェクトをしてPJSIPクライアントで携帯電話を受信して過程の中で送ってきたDTMFのボタンの情報を通じて(通って)使って、ちょうど少しの資料を調べてpjsipが相応の方法があることを発見して、pjsua_config  cfg;この構造体は、このコールバック関数cfgを設定.cb.on_call_stateは実現できるようで、その後実践し始めました.pjsipクライアント+freeswitch+x-liteで試験を行い、試験はかなり順調で、気持ちがわくわくしますね.しかし、興奮して間もなく、私が正式な運営環境に置いて私のpjsipクライアント+IMS事業者+実際の携帯電話でテストを行う時、徹底的に馬鹿になって、携帯電話のボタン情報を検出することができなくて、資料を調べてやっと携帯電話が送信したDTMF情報がin-band DTMF no rfc 2833に属するべきであることを発見して、悲しいことにPJSIP公式はこのDTMFフォーマットを支持していないことです.
一ヶ月以上の振り回されてやっと結果が出たのをあきらめなかったのはよかった.
PJSIP公式サイトのFAQはin-band DTMF no rfc 2833問題に対して解釈があって、私はあっさりしないで、直接リンクにあげますhttp://trac.pjsip.org/repos/wiki/FAQ#tone-detect  .説明はとても簡単で、簡単に見えますが、私の初心者にとって、私はほほほするしかありません.もうすぐ1ヶ月の資料を調べてみると、3、4つの方法を発見して、1つ1つ試して、1つの成功がなくて、気持ちが憂鬱ですね.結局厚かましくて、自分の腐った英語のメールで外国の友达に聞いた.幸いなことに、外国人の友人はとても親切で、一日待ってから返事が来ました.ここでEize Slangeというお兄さんに感謝します.すぐに返事をしただけでなく、対応する実装コードもくれました.ありがとうございます.
与えられたコードを参照しながら実装するのはやはり有効で、嬉しいです!!
彼が与えたサンプルコードを簡単に紹介しましょう.
in-band DTMFキー情報を取得するにはsipクライアントが受信したRTPストリームデータを検出し,RTPデータストリームに対してSpandspのライブラリを用いてその中のDTMFキー情報を解析し,大きな成果を収めた.
コードプロセスは次のとおりです.
1.pjsipクライアント層
on_call_media_stateのコールバック関数でコードを変更するには、次のようにします.
Note: pCallData / callData is some own class holding some info per call...


callback: on_call_media_state:

...
    /* Handle media status */
    switch (call_info.media_status) 
    {
      case PJSUA_CALL_MEDIA_ACTIVE:
 ...

 pj_status_t   result     = PJ_SUCCESS;
 pjmedia_port* media_port = NULL;
 unsigned      slot_port  = 0;
 pjsua_dtmfdet_config dtmf_cfg;

 // Only attach once...              
 if ( NULL == callData->dtmfData.media_port )
 {
dtmf_cfg.call_id          = call_id;
dtmf_cfg.filter_dialtone  = PJ_FALSE;
dtmf_cfg.twist            = 8;   // Twist is set to 8 dB by default.
dtmf_cfg.reverse_twist    = 4;   // Reverse twist is set to 4 dB by default. This value can be safely increased up to 6 or 7 without a significant increase in talk-off to allow DTMFs that exceed this threshold to be detected.
dtmf_cfg.threshold        =-42;  // Threshold is set to -42 dBm0 by default.
dtmf_cfg.cb.on_dtmf_digit = &on_ib_dtmf_callback;    

log_writer(SLL_EVENT, "StackDll:on_call_media_state: Attach INBAND DTMF detection");
result =  pjsua_attach_inband_dtmf_detector( call_id,
 app_config.media_cfg.clock_rate, 
app_config.media_cfg.channel_count,
160,      // samples per frame
 16,       // bits per sample
&dtmf_cfg,
PJ_FALSE, // because this called from pjsua-callback
&media_port,
&slot_port );

if ( PJ_SUCCESS == result )
{
 callData->dtmfData.media_port = media_port;
 callData->dtmfData.slot_port  = slot_port;
}
...


 
callback: on_stream_destroyed::

...
    // Release/reset Inband DTMF
    pCallData = CallGetCallData(call_id);
    if ( NULL != pCallData )
    {
      if ( NULL != pCallData->dtmfData.media_port )
      {
        log_writer(SLL_LEVEL_2, "StackDll:on_stream_destroyed> Detach INBAND detector...");
        pjsua_detach_inband_dtmf_detector( pCallData->dtmfData.media_port,
                                           pCallData->dtmfData.slot_port,
                                           PJ_FALSE); // because called from callback
      }

      pCallData->dtmfData.detectedOutbound = PJ_FALSE;
      pCallData->dtmfData.lastDigit        = -1;
      pCallData->dtmfData.media_port       = NULL;
      pCallData->dtmfData.slot_port        = 0;
    }
...
static void on_ib_dtmf_callback(pjsua_call_id call_id, int dtmf)
{
  // Your callback code when inband DTMF digit has been detected.
  // Note: this is the same callback prototype as the normal DMTF callback of PJSIP,
  //       but to distinguish between RFC2833 DTMF and this inband a dedicated
  //       callback can be created.
}

2.pjsua.h新規は以下の通りである.
pjsua-lib\pjsua.h
-----------------
...
/**
 * Functionalities around IN-BAND DTMF detection. 
 */
typedef struct pjsua_dtmfdet_callback
{
  void (*on_dtmf_digit)(pjsua_call_id call_id, int digit);

} pjsua_dtmfdet_callback;


typedef struct pjsua_dtmfdet_config
{
  pjsua_call_id           call_id;
  pj_bool_t               filter_dialtone;
  int                     twist;            // dB
  int                     reverse_twist;    // dB
  int                     threshold;        // dBm0
  pjsua_dtmfdet_callback  cb;

} pjsua_dtmfdet_config;

PJ_DECL(pj_status_t) 
pjsua_attach_inband_dtmf_detector
(
  pjsua_call_id         call_id,
  unsigned              clock_rate,
  unsigned              channel_count, 
  unsigned              samples_per_frame, 
  unsigned              bits_per_sample,
  pjsua_dtmfdet_config* config,
  pj_bool_t             needs_lock, 
  pjmedia_port        **media_port, /* OUT */
  unsigned             *slot_port   /* OUT */
);

PJ_DEF(pj_status_t) pjsua_detach_inband_dtmf_detector
(
  pjmedia_port         *media_port, 
  unsigned              slot_port,  
  pj_bool_t             needs_lock
);

3.pjsua_でmedia.cに次のように追加されました.
pjsua-lib\pjsua_media.c
-----------------------

 * Inband DTMF detection.
 */
PJ_DEF(pj_status_t) pjsua_attach_inband_dtmf_detector
(
  pjsua_call_id         call_id,
  unsigned              clock_rate,
  unsigned              channel_count, 
  unsigned              samples_per_frame, 
  unsigned              bits_per_sample,
  pjsua_dtmfdet_config* config,
  pj_bool_t             needs_lock,
  pjmedia_port        **media_port, /* OUT */
  unsigned             *slot_port   /* OUT */
)
{
    unsigned slot;
    pj_pool_t *pool;
    pjmedia_port *port;
    pj_status_t status;

    const pj_str_t name = pj_str("dtmfdet");

    if ( needs_lock ) PJSUA_LOCK();

    pool = pjsua_pool_create("dtmfdet", 1000, 1000);
    if (!pool) {
        if ( needs_lock ) PJSUA_UNLOCK();
        return PJ_ENOMEM;
    }

    status = pjmedia_dtmfdet_port_create( pool, 
                                          clock_rate, 
                                          channel_count,
                                          samples_per_frame, 
                                          bits_per_sample,
                                          config,
                                         &port);

    if (status != PJ_SUCCESS) {
        if ( needs_lock ) PJSUA_UNLOCK();
        pj_pool_release(pool);
        return status;
    }

    status = pjmedia_conf_add_port(pjsua_var.mconf, pool, 
                                   port, &name, &slot);

    if (status != PJ_SUCCESS) {
        pjmedia_port_destroy(port);
        if ( needs_lock ) PJSUA_UNLOCK();
        pj_pool_release(pool);
        return status;
    }
    
    status = pjsua_conf_connect( pjsua_call_get_conf_port(call_id),
                                 slot );

    if (status != PJ_SUCCESS) {
        pjmedia_conf_remove_port(pjsua_var.mconf, slot); 
        pjmedia_port_destroy(port);
        if ( needs_lock ) PJSUA_UNLOCK();
        pj_pool_release(pool);
        return status;
    }

    // Finalizing
    if (media_port)*media_port = port;
    if (slot_port) *slot_port  = slot;

    if ( needs_lock ) PJSUA_UNLOCK();

    return PJ_SUCCESS;
}

PJ_DEF(pj_status_t) pjsua_detach_inband_dtmf_detector
(
  pjmedia_port         *media_port, 
  unsigned              slot_port,  
  pj_bool_t             needs_lock
)
{
  if ( needs_lock ) PJSUA_LOCK();

  pjmedia_conf_remove_port(pjsua_var.mconf, slot_port); 
  pjmedia_port_destroy(media_port);

  if ( needs_lock ) PJSUA_UNLOCK();

  return PJ_SUCCESS;
}

4.pjmediaライブラリにファイルを追加するには、次のようにします.
And the new plugin, located in pjmedia/src
I named it ib_dtmfdet_port.c.
Here you interface towards SpanDSP, but I moved all required functions into a new file
--------------------------------------------------------------------------------------

#include "pjmedia/ib_dtmfdet_port.h"
//#include "pjmedia/dtmf_spandsp.h"   //      

#include 
#include 
#include 

//      spandsp      
#include 
#include 

#define THIS_FILE "ib_dtmfdet_port.c"
//#define SIGNATURE PJMEDIA_PORT_SIGNATURE('D','T','M','F')   //             
#define SIGNATURE PJMEDIA_SIG_PORT_CB

struct dtmfdet_port{pjmedia_port base;pjsua_dtmfdet_config cfg;//dtmf_rx_state_t state;//このパラメータをこのように するとコンパイルが らないのは、1つのタイプしか されていないから?よくわかりませんが、 の に えればいいようです
 
     
  
  dtmf_rx_state_t      *state;
};static void dtmfdet_digits_rx (void *user_data, const char *digits, int len);static pj_status_t dtmfdet_put_frame (pjmedia_port *this_port, const pjmedia_frame *frame);static pj_status_t dtmfdet_get_frame (pjmedia_port *this_port, pjmedia_frame *frame);static pj_status_t dtmfdet_destroy (pjmedia_port *this_port);PJ_DEF(pj_status_t) pjmedia_dtmfdet_port_create( pj_pool_t *pool, unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame, unsigned bits_per_sample, pjsua_dtmfdet_config* cfg, pjmedia_port **p_port){ const pj_str_t name = pj_str("dtmfdet"); struct dtmfdet_port *dport; PJ_ASSERT_RETURN(pool && clock_rate && p_port, PJ_EINVAL); PJ_ASSERT_RETURN(clock_rate == 8000, PJ_EINVAL); PJ_ASSERT_RETURN(bits_per_sample == 16, PJ_EINVAL);//Create and initialize port dport = PJ_POOL_ZALLOC_T(pool, struct dtmfdet_port); PJ_ASSERT_RETURN(dport != NULL, PJ_ENOMEM);// のはすべて り えます//pjmedia_port_info_init(&dport->base.info, &name, SIGNATURE, clock_rate,//channel_count, bits_per_sample, samples_per_frame);//dtmf_rx_init(&dport->state, dtmfdet_digits_rx, dport);//Configure port//dport->cfg = *cfg;//dtmf_rx_parms(&dport->state, dport->cfg.filter_dialtone, dport->cfg.twist, dport->cfg.reverse_twist, dport->cfg.threshold);
 
     

dport->state = dtmf_rx_init(dport->state, dtmfdet_digits_rx, dport);


	 //Configure port
	dport->cfg = *cfg;


	dtmf_rx_parms(dport->state, dport->cfg.filter_dialtone,
		dport->cfg.twist, 
		dport->cfg.reverse_twist,
		dport->cfg.threshold);
  // Media port interface
  dport->base.put_frame  = &dtmfdet_put_frame;
  dport->base.get_frame  = &dtmfdet_get_frame;
  dport->base.on_destroy = &dtmfdet_destroy;

  *p_port = &dport->base;

  PJ_LOG(3, (THIS_FILE, "Inband DTMF detector '%.*s' created: filter_dialtone=%s, twist=%d, reverse_twist=%d, threshold=%d",
    (int) dport->base.info.name.slen, dport->base.info.name.ptr,
    dport->cfg.filter_dialtone ? "yes" : "no",
    dport->cfg.twist,
    dport->cfg.reverse_twist,
    dport->cfg.threshold
    ));

  return PJ_SUCCESS;
}

static void dtmfdet_digits_rx(void *user_data, const char *digits, int len)
{
  struct dtmfdet_port *dport = (struct dtmfdet_port*) user_data;
  int i;

  PJ_LOG(3, (THIS_FILE, "Inband digit(s) received: '%.*s'", len, digits));

  if (dport->cfg.cb.on_dtmf_digit != NULL) {
    for (i = 0; i < len; i++) {
      dport->cfg.cb.on_dtmf_digit(dport->cfg.call_id, digits[i]);
    }
  }
}

static pj_status_t dtmfdet_put_frame(pjmedia_port *this_port, const pjmedia_frame *frame)
{
  struct dtmfdet_port *dport = (struct dtmfdet_port*) this_port;

  if (frame->type == PJMEDIA_FRAME_TYPE_AUDIO) {

	//    dtmf_rx(&dport->state, (const pj_int16_t*) frame->buf, dport->base.info.samples_per_frame); //  
	dtmf_rx(dport->state, (const pj_int16_t*) frame->buf, 160/*dport->base.info.samples_per_frame*/); //           ,          
  }

  return PJ_SUCCESS;
}

static pj_status_t dtmfdet_get_frame(pjmedia_port *this_port, pjmedia_frame *frame)
{
  PJ_UNUSED_ARG(this_port);
  PJ_UNUSED_ARG(frame);

  return PJ_EINVALIDOP;
}

static pj_status_t dtmfdet_destroy(pjmedia_port *this_port)
{
  PJ_UNUSED_ARG(this_port);

  return PJ_SUCCESS;
}

するhファイル のバイトは を しません
#ifndef __IB_DTMFDET_PORT_H__
#define __IB_DTMFDET_PORT_H__

//#include "pjsua-lib/pjsua.h"
#include "pjsua-lib/pjsua.h"
#include 
PJ_BEGIN_DECL



PJ_DEF(pj_status_t) pjmedia_dtmfdet_port_create
(
 pj_pool_t *pool, 
 unsigned clock_rate,
 unsigned channel_count, 
 unsigned samples_per_frame, 
 unsigned bits_per_sample,
 pjsua_dtmfdet_config* cfg, 
 pjmedia_port **p_port
 );



PJ_END_DECL


#endif	/* __IB_DTMFDET_PORT_H__ */

にSpandspダイナミックライブラリに することを えておいてください.ネット ではちょっと しにくいようですが、Freeswitchソースコードを ダウンロードすることができます. にはspandspソースコードとダイナミックライブラリがあります.これで したpjsipライブラリはin-band DTMF を できます.
また、PJSIPのFAQでin-band DTMFボタンを する のサンプルコードを つけることができ、これで でシミュレーションすることができ、 な でなくてもいいですが、100% できるかどうかは されていません. 、アナログのDTMFボタンです.http://trac.pjsip.org/repos/wiki/FAQ#inband-dtmfこれはpjsipがDTMFを するリンクアドレスです.
しい に しくて ヶ も しくて、このような さな は に し をかいていますね. は が した の がありますが、 を して ってみましょう.
A. URLは の り:http://article.gmane.org/gmane.comp.voip.pjsip/19289/match=pjsip+inband+dtmfという の もinband DTMFの を したようですが、 はずっと のコールバック を び して にオーバーフローして、 が があれば してみてください.
B. は を べているうちにpjsuaのように えた.media_config media_config; で したmedia_config.on_aud_prev_play_frame = &play_frame;コールバック はplayback のframeを できますが、 がSpandspで したとき、DTMF を できませんでした. が なのか かりません.
C.
URLは の りです.http://comments.gmane.org/gmane.comp.voip.pjsip/9276 はこの を しましたが、 は
pjmedia_mem_player_createという はplayback のframeを しますが、 も しませんでした. はpjmediaにいると います.mem_player_create のbuff で した は、コールバック が び されているためです. がトラブルを こすことができますか? は に しめられて にそうです.
このようにしましょう.レベルが られているので、もっと を ってください.ははは!