PJSUA Function Flow-Media
기본 미디어 포트 등록
app_init() : pjsip-apps/src/pjsua/pjsua_app.c
…
재생 및 녹화 파일 등록-
 - pjsua_player_create() -> pjmedia_wav_player_port_create(), pjsua_conf_add_port()
 - pjsua_recorder_create() -> pjmedia_wav_writer_port_create(), pjsua_conf_add_port()
미디어포트 생성
 - pjmedia_tonegen_create2(..., “tone-freq1,freq2”, …), pjsua_conf_add_port(..., tport, ...)
 - pjmedia_tonegen_create2(..., “ringback”, …), pjsua_conf_add_port(..., ringback_port, ...)
 - pjmedia_tonegen_create2(..., “ring”, …), pjsua_conf_add_port(..., ring_port, …)
전송포트 생성
 - pjsua_transport_create(PJSIP_TRANSPORT_UDP, ...)
 - pjsua_transport_create(PJSIP_TRANSPORT_TCP, ...)
 - pjsua_transport_create(PJSIP_TRANSPORT_TLS, …)
오디오장치 설정 및 스트림 시작흐름
전화발신시
*Make outgoing call to the specified URI using the specified account.
pjsip/src/pjsua-lib/pjsua_call.c :: pjsua_call_make_call()
  * Create outgoing dialog: 
  ->pjsip_dlg_create_uac()
  * Outgoing call callback when media transport creation is completed
  -> pjsua_call.c  :: on_make_call_med_tp_complete()
    * Create UAC invite session
    -> pjsip/src/pjsip-ua/sip_inv.c :: pjsip_inv_create_uac()  ->  pjsip_dlg_add_usage()
* Initialize invite session callback
pjsip/src/pjsua-lib/pjsua_call.c:
...
 inv_cb.on_media_update = &pjsua_call_on_media_update;
...
* Initialize invite session module.
pjsip_inv_usage_init(pjsua_var.endpt, &inv_cb)
inv_check_sdp_in_incoming_msg() : Check in incoming message for SDP offer/answer.
->> inv_negotiate_sdp() : Initiate SDP negotiation in the SDP negotiator.
->> mod_inv.cb.on_media_update)(inv, status)
전화수신시
mod-pjsua.on_rx_request()-> app_config.cfg.cb.on_incoming_call()-> 
pjsip_inv_answer()->inv_negotiate_sdp()->inv_cb.on_media_update() ->  app_config.cfg.cb.on_call_media_state()
 -> on_call_audio_state() ->pjsua_conf_cnnect()
 -> pjsua_set_snd_dev()
  ->> pjmedia_snd_port_create2()
PJSUA에서 호출되지 않는 포트생성 함수
컨퍼런트 포트 생성시
pjmedia_snd_port_create() 
- pjmedia_conf_create()에서 pjmedia_conf.options에 PJMEDIA_CONF_NO_DEVICE 이 없으면 수행된다. 하지만 pjsua는 PJMEDIA_CONF_NO_DEVICE이 기본으로 설정되어 호출되지 않는다.
  -> param.base.dir = PJMEDIA_DIR_CAPTURE_PLAYBACK
  ->  param.base.rec_id = dev_id;
   -> param.base.play_id = dev_id;
  -> pjmedia_snd_port_create2() <<<<<
레코딩만 하는 경우
pjmedia_snd_port_create_rec()   
 :pjmedia/src/pjmedia/sound_port.c
  :캡춰만 할경우 호출된다.
  -> param.base.dir = PJMEDIA_DIR_CAPTURE;
  -> param.base.rec_id = dev_id;
  -> pjmedia_snd_port_create2(param, p_port) <<<<<
플레이만 하는 경우
pjmedia_snd_port_create_player() 
  : 플레이만 할 경우 호출된다. 예를 들면 ring, ringback, play-file의 경우.
  -> param.base.dir = PJMEDIA_DIR_PLAYBACK;
  -> param.base.play_id = dev_id;
  -> pjmedia_snd_port_create2() <<<<<
오디오장치 설정 및 스레드 시작
pjmedia_snd_port_create2(pool, prm, p_port) 
  -> snd_port->rec_id = prm->base.rec_id 등 prm을 snd_port에 채움.
  -> pjmedia_clock_src_init(snd_port->cap_clocksrc,snd_port->clock_rate..)
  -> pjmedia_clock_src_init(snd_port->play_clocksrc,snd_port->clock_rate..)
  -> start_sound_device(pool, snd_port)
    ->> pjmedia_aud_stream_create(pjmedia_aud_param, rec_cb, play_cb...)  :pjmedia/src/pjmedia/audiodev.c
          - 오디오장치 오픈 및 placy_cb, rec_cb 콜백등록
         ->>> f->op->create_stream(f, ¶m, rec_cb, play_c...)
    ->> pjmedia_snd_port_set_ec() - AEC 설정
    ->> pjmedia_aud_stream_start(snd_port->aud_stream)     :pjmedia/src/pjmedia/sound_port.c   
          - record/capture 스레드 시작
pjmedia_aud_dev_factory_op{
  ....
  create_stream = alsa_factory_create_stream()
}
alsa_factory_create_stream()  :pjmedia/src/pjmedia-audiodev/alsa_dev.c
  -> stream->pb_cb     = play_cb;
     stream->ca_cb     = rec_cb;
     stream->base.op = &alsa_stream_op;
  -> open_playback (stream, param)
    ->> snd_pcm_open()
    ->> snd_pcm_hw_params_set_period_size_near() 
    ->> snd_pcm_hw_params()
  -> open_capture (stream, param)
    ->> snd_pcm_open()
    ->> snd_pcm_hw_params_set_period_size_near() 
    ->> snd_pcm_hw_params()
static pjmedia_aud_stream_op alsa_stream_op =  :src/pjmedia-audiodev/alsa_dev.c
{
  ...
  set_cap = alsa_stream_get_cap
  start = alsa_stream_start,
  stop = alsa_stream_stop,
}
버퍼제어
alsa_stream_start(pjmedia_aud_stream *s) : pjmedia/src/pjmedia-audiodev/alsa_dev.c
  :struct alsa_stream *stream = s;
  -> pj_thread_create("alsasound_playback", pb_thread_func,stream...)
     ->> stream->pb_cb (user_data, &frame)
     ->> snd_pcm_writei (pcm, buf, nframes) -EPIPE 실패시::q  "pb_thread_func: underrun!" :확인해볼것!
  -> pj_thread_create("alsasound_capture", ca_thread_func,stream...)
     ->> snd_pcm_readi (pcm, buf, nframes)
     ->> stream->ca_cb (user_data, &frame)  -EPIPE 실패시: "ca_thread_func: overrun!"
rec_cb(void *user_data, frame)     : pjmedia/sound_port.c
  : sound recoder 가 캡춰를 수행 후 이 함수를 호출한다.
  -> pjmedia_clock_src_update(snd_port->cap_clocksrc, frame->timestamp)
  -> pjmedia_ehco_cpature(frame->buf)
  -> pjmedia_port_put_frame(snd_port->port, frame)  :pjmedia/port.c
play_cb(void *user_data, frame) : pjmedia/sound_port.c
  : sound player thread가 재생할 샘플이 필요할때 이 함수를 호출한다.  
  -> pjmedia_port_get_frame(snd_port->port, frame);
  -> pjmedia_clock_src_update(snd_port->play_clocksrc, frame->timestamp         
  -> pjmedia_echo_playback(fram->buf)
네트워크 스트림 포트생성
PJSUA log -------------------------------------------------------------
00:14:15.366    pjsua_aud.c  ..Conf connect: 3 --> 0
00:14:15.367    pjsua_aud.c  ...Set sound device: capture=-1, playback=-2
00:14:15.367    pjsua_app.c  ....Turning sound device ON
00:14:15.367    pjsua_aud.c  ....Opening sound device (speaker + mic) PCM@16000/1/20ms
00:14:15.442   ec0xb5a13ce0  .....AEC created, clock_rate=16000, channel=1, samples per frame=320, tail length=200 ms, latency=0 ms
00:14:15.443   conference.c  ...Port 3 (ring) transmitting to port 0 (default:CARD=SAMA5D3EK)
00:14:15.443   pjsua_call.c  ..Answering call 0: code=200
00:14:15.445  pjsua_media.c  .....Call 0: updating media..
00:14:15.447    pjsua_aud.c  ......Audio channel update..     <<<<<
---------------------------------------------------------------------------------------
전화수신시 함수 호출순서:
mod-pjsua.on_rx_request()-> app_config.cfg.cb.on_incoming_call()-> 
pjsip_inv_answer()->inv_negotiate_sdp()->inv_cb.on_media_update() ->  app_config.cfg.cb.on_call_media_state()
-> pjsua_aud_channel_update()
pjsua_aud_channel_update()  :pjsip/src/pjsua-lib/pjsua_aud.c  <<<<<<
  -> pjmedia_stream_create()   :/pjmedia/src/pjmedia/stream.c
    ->> stream = PJ_POOL_ZALLOC_T(pool, pjmedia_stream): 생성하고 이 함수에서 값을 채운다.
    ->> pjmedia_port_info_init(stream->port, clock_rate, channel_cnt, bitpersample..) 
    ->> pjmedia_codec_init(stream->codec)
    ->> stream->port.put_frame = &put_frame
    ->> stream->port.get_frame = &get_frame_ext;
    ->> pjmedia_jbuf_create()
    ->> create_channel(..,PJMEDIA_DIR_DECODING, stream->dec)
    ->> create_channel(..,PJMEDIA_DIR_ENCODING, stream->enc)
    ->> pjmedia_rtcp_init2()
  -> pjmedia_stream_start(call_med->strm.a.stream) 
     : stream->enc->paused = 0;
  -> pjmedia_stream_get_port(call_med->strm.a.stream, &media_port)
     : media_port 인터페이스 전달.(stream->port)
  -> pjmedia_conf_add_port(media_port, conf_slot)  :pjmedia/src/pjmedia/conference.c
     : conference bride에 전화스트림 연결.
    ->> create_conf_port(pool, conf, strm_port, port_name, &conf_port)
      ->>> pjmedia_resample_create() for Rx Buffer
      ->>> pjmedia_resample_create() for Tx Buffer
      ->>> conf_port->rx_buf = alloc()
      ->>> conf_port->rx_buf = alloc()
      ->>> conf_port->mix_buf = alloc()
버퍼제어
    ->> port->put_frame(port, frame)    :pjmedia/stream.c
         : pcm을 인코딩하고 RTP 패킷으로 만든다.(upstream)
         ->>> rebuffer(stream, frame) : frame-buf를 steam->enc_buf로 이동
         ->>> put_frame_imp(port, frame)
             ->>>> pjmedia_codec_encode(stream->codec, frame_in, size, frame_out);
             ->>>> pjmedia_rtp_encode_rtp(channel->rtp, channel->pt,...)
                  : frame_out = channel->out_pkt
    : fram_in = frame
  -> pjmedia_port_get_frame(port, frame)  :pjmedia/port.c
    ->> port->get_frame(port, frame)
       : jitter buffer에서 정상적인 프레임을 가져와 디코딩한다.
       ->>> pjmedia_jbuf_get_frame2(stream->jb, channel->out_pkt, frame_size,....)       
       ->>> pjmedia_codec_decode(stream->codec, frame_in, size, frame_out)
            :frame_in.buf = channel->out_pkt
            :frame_out.buf = frame_out.buf = frame->buf + samples_count;
Coference Bridge
전화수신시 또는 파일 재생 연결시 포트생성:
PJSUA log -------------------------------------------------------------------------------------
00:14:10.096 sip_endpoint.c  Module "mod-default-handler" registered
00:14:10.096    pjsua_aud.c  Creating file player: /mnt/TheCubanMissileCrisis_8K.wav..
00:14:10.113   wav_player.c  .File player '/mnt/TheCubanMissileCrisis_8K.wav' created: samp.rate=8000, ch=1, bufsize=4KB, filesize=7264KB
00:14:10.113    pjsua_aud.c  .Player created, id=0, slot=1
.....
>>> cc 1 0
00:29:53.854    pjsua_aud.c !Conf connect: 1 --> 0
00:29:53.854    pjsua_aud.c  .Set sound device: capture=-1, playback=-2
00:29:53.854    pjsua_app.c  ..Turning sound device ON
00:29:53.854    pjsua_aud.c  ..Opening sound device (speaker + mic) PCM@16000/1/20ms
00:29:53.930     ec0x1bf8c8  ...AEC created, clock_rate=16000, channel=1, samples per frame=320, tail length=200 ms, latency=0 ms
00:29:53.930   conference.c  .Port 1 (/mnt/TheCubanMissileCrisis_8K.wav) transmitting to port 0 (default:CARD=SAMA5D3EK)
-----------------------------------------------------------------------------------------------------
pjsua_init()
pjsua_media_subsys_init()   :src/pjsua-lib/pjsua_media.c
  -> pjsua_aud_subsys_init()   :pjsua_aud.c
    ->> pjmedia_conf_create()   :pjmedia/src/pjmedia/conference.c
      ->>> conf->master_port->get_frame = get_frame;
      ->>> conf->master_port->put_frame = &put_frame;
      ->>> create_sound_port() -  name:"Master/sound"
      **** device 의 play_cb, rec_cb에서 master_port의 get_frame, put_frame을 호출한다.
conf->master_port->put_frame()
-> conference :: put_frame()      
  -> read_port() :      :pjmedia/src/pjmedia/conference.c
    :Read from port.
    ->> pjmedia_port_get_frame(cport->port, frame) :pjmedia/port.c
conf->master_port->get_frame()
-> conference :: get_frame(frame)  : stream port에서 가져운 frame
  수신 대기중인 포트에 가져온 프레임을 전달한다.
  -> 등록된 모든포트(conf->ports[i])에서 수신대기포트의 mix_buff에 signal을 섞는다.
  -> write_port(conf, conf->ports[]i]) x conf->port_cnt  :pjmedia/src/pjmedia/conference.c
    : 믹스된 신호를 port로 전달한다.
    : 믹스된 신호는 32bit다. mix buffer에 샘플이 있으면 32비트->16비트로 변경한다.
    : 레벨 변경이 필요하다면 tx signal에 적용한다.
    ->> frame.buf = cport->tx_buf
    ->> pjmedia_portput_frame(cport->port, frame)  :pjmedia/port.c
        : coference에 등록된 포트에 쓴다.
  -> pjmedia_copy_samples(frame->buf, conf->ports[0]->mix_buf,conf->samples_per_frame)
 
댓글 없음:
댓글 쓰기