问题:非代理模式下,视频媒体主叫如果是recvonly,主叫收不到被叫发过来的视频包;
分析:主叫是recvonly时,被叫是sendonly,最开始以为是跟主被叫的rmode有关系,最后发现还真是有关系;
tcpdump抓包分析sendonly方明确有发包,但并没有转出去的包,所以分析可以明确是freeswitch没有转包!
freeswitch为什么没有转包呢,连同发现有下面几个问题:
1、发送方的payload type和协商的不一致,freeswitch过滤了payload type不一致的包;
代码语言:javascript复制#if 1//lyz for payload error.
if (bytes && !rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] &&
rtp_session->last_rtp_hdr.pt != 13 &&
rtp_session->last_rtp_hdr.pt != rtp_session->recv_te &&
rtp_session->last_rtp_hdr.pt != rtp_session->cng_pt) {
int accept_packet = 1;
int required_pt = 0;
if (!(rtp_session->rtp_bugs & RTP_BUG_ACCEPT_ANY_PAYLOAD) &&
!(rtp_session->rtp_bugs & RTP_BUG_ACCEPT_ANY_PACKETS) && rtp_session->pmaps && *rtp_session->pmaps) {
payload_map_t *pmap;
accept_packet = 0;
switch_mutex_lock(rtp_session->flag_mutex);
for (pmap = *rtp_session->pmaps; pmap && pmap->allocated; pmap = pmap->next) {
if (ntohl(*(int *)(b 4)) == ZRTP_MAGIC_COOKIE) {
accept_packet = 1;
break;
}
if (!pmap->negotiated) {
continue;
}
if (rtp_session->last_rtp_hdr.pt == pmap->pt) {
accept_packet = 1;
if (pmapP) {
*pmapP = pmap;
}
break;
}else{
required_pt = pmap->pt;
}
}
switch_mutex_unlock(rtp_session->flag_mutex);
}
if (!accept_packet) {
if (rtp_session->recv_msg.header.pt != required_pt && required_pt != 0){
rtp_session->recv_msg.header.pt = required_pt;//lyz force modify
}else{
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_INFO,
"Invalid Packet SEQ: %d TS: %d PT:%d ignoredn",
ntohs(rtp_session->recv_msg.header.seq), ntohl(rtp_session->last_rtp_hdr.ts), rtp_session->last_rtp_hdr.pt);
*bytes = 0;
}
}
}
#endif//lyz for payload type 不一致
2、freeswitch接收到一方的包,解码完后,并没有转发给对方;
代码语言:javascript复制SWITCH_DECLARE(switch_status_t) switch_core_session_write_video_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags,
int stream_id)
#if 0//del for need write frame to recvonly
if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_RECVONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_INACTIVE) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Writing video to RECVONLY/INACTIVE sessionn");
return SWITCH_STATUS_SUCCESS;
}
#else
if (switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY || switch_core_session_media_flow(session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_INACTIVE) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Writing video to RECVONLY/INACTIVE sessionn");
return SWITCH_STATUS_SUCCESS;
}
#endif//
为啥代理模式下freeswitch能转发给recvonly的主叫方呢,原因是代理模式下并没有设置media_flow,所以并没有走这个分支;
3、freeswitch作为背靠背代理,如何进行调试?
所谓背靠背代理,就是每通呼叫会创建有aleg和bleg两个会话,每个会话都有一个channel处理rtp媒体数据;
aleg接收a发送的数据,然后转发给b;
bleg接收b发过来的数据,然后转发给a;
所以相同的程序a_leg和b_leg会同时运行,那么如何调试a_leg而不是b_leg的问题呢?
方法1:gdb attach绑定到freeswitch的进程,然后设置条件断点进行调试分析;
方法2:打印switch_channel_get_name可以区分不同的session和channel;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(vh->session_a), SWITCH_LOG_DEBUG, "%s video thread ended.n", switch_channel_get_name(channel));
2022-05-10 14:21:31.455431 [DEBUG] switch_core_media.c:7594 sofia/internal/4002-0xb202c1f8@192.168.16.102:50450 Video thread ended
freeswitch代码逻辑还是相当复杂的,一个函数通常都是上千行的代码,所以跟踪分析问题并不是那么容易,最好是gdb结合log打印跟踪分析问题;
4、视频的转发堆栈;
代码语言:javascript复制#0 switch_core_session_write_video_frame (session=0x15039c8, frame=0x7f343007f920, flags=flags@entry=0, stream_id=stream_id@entry=0)
at src/switch_core_media.c:14554
#1 0x00007f345cdb416d in video_bridge_thread (session=0x7f3430068db8, obj=0x7f343409e790) at src/switch_ivr_bridge.c:242
#2 0x00007f345cd707ce in video_helper_thread (thread=<optimized out>, obj=0x7f3430084470) at src/switch_core_media.c:7548
#3 0x00007f345d03c74c in dummy_worker (opaque=0x7f343c1ca4e0) at threadproc/unix/thread.c:151
#4 0x00007f345a74f17a in start_thread () from /lib64/libpthread.so.0
#5 0x00007f3459d01dc3 in clone () from /lib64/libc.so.6
(gdb) c
视频收包,解码逻辑:
代码语言:javascript复制Thread 33 "freeswitch" hit Breakpoint 1, switch_h264_decode (codec=0x2425598, frame=0x2425500) at avcodec.c:1806
1806 h264_codec_context_t *context = (h264_codec_context_t *)codec->private_info;
(gdb) bt
#0 switch_h264_decode (codec=0x2425598, frame=0x2425500) at avcodec.c:1806
#1 0x00007efc2c450422 in switch_core_codec_decode_video (codec=0x2425598, frame=0x2425500) at src/switch_core_codec.c:864
#2 0x00007efc2c4a2d01 in switch_core_session_read_video_frame (session=0x26a6c98, frame=frame@entry=0x7efc04072be8, flags=flags@entry=0,
stream_id=stream_id@entry=0) at src/switch_core_media.c:14974
#3 0x00007efc2c4e7055 in video_bridge_thread (session=0x26a6c98, obj=0x7efbfb142790) at src/switch_ivr_bridge.c:227
#4 0x00007efc2c4a372e in video_helper_thread (thread=<optimized out>, obj=0x242a050) at src/switch_core_media.c:7548
#5 0x00007efc2c76f6ac in dummy_worker (opaque=0x7efc000789b0) at threadproc/unix/thread.c:151
#6 0x00007efc29e8217a in start_thread () from /lib64/libpthread.so.0
#7 0x00007efc29434dc3 in clone () from /lib64/libc.so.6
音频媒体流的转发堆栈:
代码语言:javascript复制Thread 31 "freeswitch" hit Breakpoint 1, switch_core_media_write_frame (session=session@entry=0x7f8d6c0dda38, frame=frame@entry=0x2014148,
flags=flags@entry=0, stream_id=stream_id@entry=0, type=type@entry=SWITCH_MEDIA_TYPE_AUDIO) at src/switch_core_media.c:3419
3419 switch_assert(session);
(gdb) bt
#0 switch_core_media_write_frame (session=session@entry=0x7f8d6c0dda38, frame=frame@entry=0x2014148, flags=flags@entry=0,
stream_id=stream_id@entry=0, type=type@entry=SWITCH_MEDIA_TYPE_AUDIO) at src/switch_core_media.c:3419
#1 0x00007f8d6aaf36d3 in sofia_write_frame (session=0x7f8d6c0dda38, frame=0x2014148, flags=0, stream_id=0) at mod_sofia.c:1235
#2 0x00007f8d75d0fb9b in perform_write (session=session@entry=0x7f8d6c0dda38, frame=0x2014148, flags=0, stream_id=0)
at src/switch_core_media.c:7157
#3 0x00007f8d75d3e47e in switch_core_session_write_frame (session=session@entry=0x7f8d6c0dda38, frame=<optimized out>, flags=flags@entry=0,
stream_id=stream_id@entry=0) at src/switch_core_media.c:16309
#4 0x00007f8d75d822fe in audio_bridge_thread (obj=obj@entry=0x7f8d6c1dce78, thread=0x0) at src/switch_ivr_bridge.c:823
#5 0x00007f8d75d84756 in switch_ivr_multi_threaded_bridge (session=session@entry=0x21a39c8, peer_session=0x7f8d6c0dda38,
input_callback=<optimized out>, session_data=session_data@entry=0x0, peer_session_data=peer_session_data@entry=0x0)
at src/switch_ivr_bridge.c:1797
#6 0x00007f8d4fbeee68 in audio_bridge_function (session=<optimized out>, data=<optimized out>) at mod_dptools.c:3672
#7 0x00007f8d75cfdcaf in switch_core_session_exec (session=session@entry=0x21a39c8,
application_interface=application_interface@entry=0x1e11f90, arg=arg@entry=0x7f8d6c12bcc0 "user/4002@${domain_name}")
at src/switch_core_session.c:2891
#8 0x00007f8d75cfe33f in switch_core_session_execute_application_get_flags (session=session@entry=0x21a39c8, app=0x7f8d6c12bcb8 "bridge",
arg=0x7f8d6c12bcc0 "user/4002@${domain_name}", flags=flags@entry=0x0) at src/switch_core_session.c:2756
#9 0x00007f8d75d02682 in switch_core_standard_on_execute (session=0x21a39c8) at src/switch_core_state_machine.c:354
#10 switch_core_session_run (session=0x21a39c8) at src/switch_core_state_machine.c:651
#11 0x00007f8d75cfb6ae in switch_core_session_thread (thread=<optimized out>, obj=0x21a39c8) at src/switch_core_session.c:1709
#12 0x00007f8d75cf70c3 in switch_core_session_thread_pool_worker (thread=0x1fc7f20, obj=<optimized out>) at src/switch_core_session.c:1772
#13 0x00007f8d7600878c in dummy_worker (opaque=0x1fc7f20) at threadproc/unix/thread.c:151
#14 0x00007f8d7371b17a in start_thread () from /lib64/libpthread.so.0
#15 0x00007f8d72ccddc3 in clone () from /lib64/libc.so.6
5、只有一路流的情况下,freeswitch转码,并且转发给rtmp服务器一路流,发现cpu占比还是很高的:
代码语言:javascript复制 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND
696684 root -2 -10 848068 144700 24264 S 15.0 3.6 23:25.80 freeswitch
视频编码都是h264,虽然开启了record,是不是也可以不做解码和编码的处理呢,下一步就是修改freeswitch在默认模式下,对视频的解码、编码处理逻辑做出修改,降低freeswitch视频转码产生的cpu持续高负载的情况;