mediastreamer2添加自定义filter实现linphone加密语音通信

mediastreamer2提供的Filter链接为Graph的方式,通过代码让使用者将各种自带的msfilter结构链接起来,使之可以灵活的定义多媒体流。通过自定义插件,可以给mediastreamer2框架加入新功能。要自定义一个模块,需要按照规范定义一系列数据结构,其中最关键的是描述插件功能的结构体MSFilterDesc,该结构体包含了插件名字、类型、输入输出端个数和处理函数。定义了结构体后实现有关函数,在mediastreamer2中,数据流通过mblk_t结构体来承载,mblk_t中有两个指针b_wptr和b_rptr,分别对应于分配给的一块内存中,开始和结束地址。当通过函数ms_queue_get从输入端数据队列获取mblk_t后,从b_rptr读取数据,处理后的数据拷贝入新建的mblk_t中,边写入边递增指针b_wptr,标记新的结束地址,直到输入端的数据包中数据处理完毕并填入到下一输出包,通过ms_queue_put将包发给下一滤镜(filter)队列,并通过freemsg将输入的mblk_t释放。

博客mediastreamer2库二次开发及添加自定义filter示例代码分析只是添加了一个自定义的filter并自己写了个测试demo来调用增加的filter。这篇博客简要介绍如何修改mediastreamer2库的源代码来实现linphone的加密语音通信。

为了让linphone能够调用增加的filter,首先需要在mediastream.h文件中对struct _AudioStream结构体的定义进行扩展,加入加密和解密filter作为结构体成员。

修改后的struct _AudioStream定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct _AudioStream
{
MSTicker *ticker;
RtpSession *session;
MSFilter *soundread;
MSFilter *soundwrite;
MSFilter *encoder;
MSFilter *decoder;
MSFilter *rtprecv;
MSFilter *rtpsend;
MSFilter *dtmfgen;
MSFilter *dtmfgen_rtp;
MSFilter *ec;/*echo canceler*/
MSFilter *volsend,*volrecv; /*MSVolumes*/
MSFilter *read_resampler;
MSFilter *write_resampler;
MSFilter *equalizer;
MSFilter *crypto; // 加密filter
MSFilter *decrypto; // 解密filter

...
};

本文来自怎么下载秒拍视频

然后在audiostream.c文件中修改audio_stream_start_full函数,修改后的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
int audio_stream_start_full(AudioStream *stream, RtpProfile *profile, const char *remip,int remport,
int rem_rtcp_port, int payload,int jitt_comp, const char *infile, const char *outfile,
MSSndCard *playcard, MSSndCard *captcard, bool_t use_ec)

{

RtpSession *rtps=stream->session;
PayloadType *pt;
int tmp;
MSConnectionHelper h;
int sample_rate;

rtp_session_set_profile(rtps,profile);
if (remport>0) rtp_session_set_remote_addr_full(rtps,remip,remport,rem_rtcp_port);
rtp_session_set_payload_type(rtps,payload);
rtp_session_set_jitter_compensation(rtps,jitt_comp);

if (remport>0)
ms_filter_call_method(stream->rtpsend,MS_RTP_SEND_SET_SESSION,rtps);
stream->rtprecv=ms_filter_new(MS_RTP_RECV_ID);
stream->decrypto = ms_filter_new_from_name("Decrypt");

ms_filter_call_method(stream->rtprecv,MS_RTP_RECV_SET_SESSION,rtps);
stream->session=rtps;

stream->dtmfgen=ms_filter_new(MS_DTMF_GEN_ID);
rtp_session_signal_connect(rtps,"telephone-event",(RtpCallback)on_dtmf_received,(unsigned long)stream);
rtp_session_signal_connect(rtps,"payload_type_changed",(RtpCallback)payload_type_changed,(unsigned long)stream);
/* creates the local part */
if (captcard!=NULL) stream->soundread=ms_snd_card_create_reader(captcard);
else {
stream->soundread=ms_filter_new(MS_FILE_PLAYER_ID);
stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
if (infile!=NULL) {audio_stream_play(stream,infile);
printf("\n\n\tread from file\n\n");
}
}
if (playcard!=NULL) stream->soundwrite=ms_snd_card_create_writer(playcard);
else {
stream->soundwrite=ms_filter_new(MS_FILE_REC_ID);
if (outfile!=NULL) audio_stream_record(stream,outfile);
}

/* creates the couple of encoder/decoder */
pt=rtp_profile_get_payload(profile,payload);
if (pt==NULL){
ms_error("audiostream.c: undefined payload type.");
return -1;
}
if (rtp_profile_get_payload_from_mime (profile,"telephone-event")==NULL
&& ( strcasecmp(pt->mime_type,"pcmu")==0 || strcasecmp(pt->mime_type,"pcma")==0)){
/*if no telephone-event payload is usable and pcma or pcmu is used, we will generate
inband dtmf*/

stream->dtmfgen_rtp=ms_filter_new (MS_DTMF_GEN_ID);
}

if (ms_filter_call_method(stream->rtpsend,MS_FILTER_GET_SAMPLE_RATE,&sample_rate)!=0){
ms_error("Sample rate is unknown for RTP side !");
return -1;
}

stream->crypto = ms_filter_new_from_name("Encrypt");

stream->encoder=ms_filter_create_encoder(pt->mime_type);
stream->decoder=ms_filter_create_decoder(pt->mime_type);
if ((stream->encoder==NULL) || (stream->decoder==NULL)){
/* big problem: we have not a registered codec for this payload...*/
ms_error("mediastream.c: No decoder available for payload %i.",payload);
return -1;
}

stream->volsend=ms_filter_new(MS_VOLUME_ID);
stream->volrecv=ms_filter_new(MS_VOLUME_ID);
audio_stream_enable_echo_limiter(stream,stream->el_type);
audio_stream_enable_noise_gate(stream,stream->use_ng);

if (stream->use_agc){
int tmp=1;
if (stream->volsend==NULL)
stream->volsend=ms_filter_new(MS_VOLUME_ID);
ms_filter_call_method(stream->volsend,MS_VOLUME_ENABLE_AGC,&tmp);
}

/* give the sound filters some properties */
if (ms_filter_call_method(stream->soundread,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
/* need to add resampler*/
if (stream->read_resampler == NULL) stream->read_resampler=ms_filter_new(MS_RESAMPLE_ID);
}

if (ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sample_rate) != 0) {
/* need to add resampler*/
if (stream->write_resampler == NULL) stream->write_resampler=ms_filter_new(MS_RESAMPLE_ID);
}

tmp=1;
ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &tmp);

/*configure the echo canceller if required */
if (use_ec) {
stream->ec=ms_filter_new(MS_SPEEX_EC_ID);
ms_filter_call_method(stream->ec,MS_FILTER_SET_SAMPLE_RATE,&sample_rate);
if (stream->ec_tail_len!=0)
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_TAIL_LENGTH,&stream->ec_tail_len);
if (stream->ec_delay!=0){
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&stream->ec_delay);
}else{
/*configure from latency of sound card in case it is availlable */
int latency=0;
ms_filter_call_method(stream->soundread,MS_FILTER_GET_LATENCY,&latency);
latency-=30; /*keep 30 milliseconds security margin*/
if (latency<0) latency=0;
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_DELAY,&latency);
}
if (stream->ec_framesize!=0)
ms_filter_call_method(stream->ec,MS_ECHO_CANCELLER_SET_FRAMESIZE,&stream->ec_framesize);
}

/* give the encoder/decoder some parameters*/
ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);
ms_message("Payload's bitrate is %i",pt->normal_bitrate);
if (pt->normal_bitrate>0){
ms_message("Setting audio encoder network bitrate to %i",pt->normal_bitrate);
ms_filter_call_method(stream->encoder,MS_FILTER_SET_BITRATE,&pt->normal_bitrate);
}
ms_filter_call_method(stream->decoder,MS_FILTER_SET_SAMPLE_RATE,&pt->clock_rate);

if (pt->send_fmtp!=NULL) ms_filter_call_method(stream->encoder,MS_FILTER_ADD_FMTP, (void*)pt->send_fmtp);
if (pt->recv_fmtp!=NULL) ms_filter_call_method(stream->decoder,MS_FILTER_ADD_FMTP,(void*)pt->recv_fmtp);

/*create the equalizer*/
stream->equalizer=ms_filter_new(MS_EQUALIZER_ID);
tmp=stream->eq_active;
ms_filter_call_method(stream->equalizer,MS_EQUALIZER_SET_ACTIVE,&tmp);
/*configure resampler if needed*/
if (stream->read_resampler){
audio_stream_configure_resampler(stream->read_resampler,stream->soundread,stream->rtpsend);
}

if (stream->write_resampler){
audio_stream_configure_resampler(stream->write_resampler,stream->rtprecv,stream->soundwrite);
}
/* and then connect all */
/* tip: draw yourself the picture if you don't understand */

/*sending graph*/
ms_connection_helper_start(&h);
ms_connection_helper_link(&h,stream->soundread,-1,0);
if (stream->read_resampler)
ms_connection_helper_link(&h,stream->read_resampler,0,0);
if (stream->ec)
ms_connection_helper_link(&h,stream->ec,1,1);
if (stream->volsend)
ms_connection_helper_link(&h,stream->volsend,0,0);
if (stream->dtmfgen_rtp)
ms_connection_helper_link(&h,stream->dtmfgen_rtp,0,0);
ms_connection_helper_link(&h,stream->encoder,0,0);
//encryption plugin
ms_connection_helper_link(&h,stream->crypto,0,0);
ms_connection_helper_link(&h,stream->rtpsend,0,-1);

/*receiving graph*/
ms_connection_helper_start(&h);
ms_connection_helper_link(&h,stream->rtprecv,-1,0);
//decryption plugin
ms_connection_helper_link(&h,stream->decrypto,0,0);
ms_connection_helper_link(&h,stream->decoder,0,0);
ms_connection_helper_link(&h,stream->dtmfgen,0,0);
if (stream->equalizer)
ms_connection_helper_link(&h,stream->equalizer,0,0);
if (stream->volrecv)
ms_connection_helper_link(&h,stream->volrecv,0,0);
if (stream->ec)
ms_connection_helper_link(&h,stream->ec,0,0);
if (stream->write_resampler)
ms_connection_helper_link(&h,stream->write_resampler,0,0);
ms_connection_helper_link(&h,stream->soundwrite,0,-1);

/* create ticker */
stream->ticker=ms_ticker_new();
ms_ticker_set_name(stream->ticker,"Audio MSTicker");
ms_ticker_attach(stream->ticker,stream->soundread);
ms_ticker_attach(stream->ticker,stream->rtprecv);
return 0;
}

注意,由于本方案只是实现了语音的加解密,故只修改了Audio对应的数据结构和函数,如果要实现视频的同样功能,可进行类似修改Video的有关内容!

下为加入了身份认证和加解密功能后的安全语音通信系统的部分截图:

mediastreamer2自定义filter

mediastreamer2源码分析

mediastreamer2二次开发