怎么在mediastreamer2库进行二次开发及添加自定义filter?附示例代码分析

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释放。

增加自定义filter

通过自定义插件,可以给mediastreamer2框架加入新功能,而本文提到的加解密模块正是通过此方式实现。

要自定义一个模块,需要按照规范定义一系列数据结构,其中最关键的是描述插件功能的结构体MSFilterDesc,该结构体包含了插件名字、类型、输入输出端个数和处理函数。

例如这里定义了本方案中实现加密功能的插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
static MSFilterDesc msencrypt_filter={ // 一个MSFilterDesc结构体
.id=MS_FILTER_PLUGIN_ID,
.name="Encrypt", // 使用该msencrypt_filter时可通过名字生成对应filter
.text="HC256 Stream EnCrypto",
.category=MS_FILTER_OTHER,
.ninputs=1,
.noutputs=1,
.init=msencrypt_init, // 自定义的初始化函数
.preprocess=msencrypt_preprocess, // 自定义的预处理函数
.process=msencrypt_process, // 自定义的处理函数
.postprocess=msencrypt_postprocess, // 自定义的尾处理函数
.uninit=msencrypt_uninit, // 自定义的清理函数
.methods=enc_methods, // 自定义的方法
.flags=MS_FILTER_IS_PUMP
};

该结构体中,函数init、preprocess、process、postprocess和uninit分别用于插件初始化、数据流开始处理前、数据流处理中、数据流结束处理前和插件卸载时。最关键的函数process函数将处理所有经过此插件的数据流。

定义了结构体后实现有关函数,在mediastreamer2中,数据流通过mblk_t结构体来承载,mblk_t中有两个指针b_wptr和b_rptr,分别对应于分配给的一块内存中,开始和结束地址。当通过函数ms_queue_get从输入端数据队列获取mblk_t后,从b_rptr读取数据,处理后的数据拷贝入新建的mblk_t中,边写入边递增指针b_wptr,标记新的结束地址,直到输入端的数据包中数据处理完毕并填入到下一输出包,通过ms_queue_put将包发给下一滤镜队列,并通过freemsg将输入的mblk_t释放。

定义好的插件需要通过以下函数载入:

1
2
3
4
5
6
void libmscrypt_init()    // 该函数的命名有一定规范要求
{

ms_filter_register(&msencrypt_filter); // 注册自定义的加密filter
ms_filter_register(&msdecrypt_filter); // 注册自定义的解密filter
ms_message("libmscrypt plugin loaded");
}

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

编译好的动态链接库.so文件(Linux系统中),若为libmscrypt.so,那么载入插件的函数名便为libmscrypt_init

将之放入于指定位置/usr/local/lib/mediastreamer/plugins目录中,便可在每次使用mediastreamer2时自动载入;也可以通过ms_load_plugins手动载入包含插件的动态链接库。

使用自定义filter

加入上述自定义加密解密插件后,就可以在需要使用该filter的客户端代码中进行调用

下面以一个简单的本地发送和本地接受的示例程序说明如何调用自定义的filter,具体代码如下:

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
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <mediastreamer2/mscommon.h>
#include <mediastreamer2/mssndcard.h>
#include <mediastreamer2/msfilter.h>
#include <mediastreamer2/msticker.h>
#include <mediastreamer2/msrtp.h>
#include <mediastreamer2/mediastream.h>
#include <mediastreamer2/msfileplayer.h>
#include <mediastreamer2/msfilerec.h>

#include <ortp/ortp.h>

#define MAX_RTP_SIZE 1500

struct mstream {
MSFilter *rtprecv;
MSFilter *decrypto;
MSFilter *decoder;
MSFilter *soundwrite;

MSFilter *soundread;
MSFilter *crypto;
MSFilter *encoder;
MSFilter *rtpsend;

MSTicker *ticker;
};

RtpSession *create_duplex_rtpsession(int locport) {
RtpSession *rtpr;
rtpr=rtp_session_new(RTP_SESSION_SENDRECV);
rtp_session_set_recv_buf_size(rtpr, MAX_RTP_SIZE);
rtp_session_set_scheduling_mode(rtpr, 0);
rtp_session_set_blocking_mode(rtpr, 0);
rtp_session_enable_adaptive_jitter_compensation(rtpr, FALSE);
rtp_session_set_symmetric_rtp(rtpr, TRUE);
rtp_session_set_local_addr(rtpr, "0.0.0.0", locport);
rtp_session_signal_connect(rtpr, "timestamp_jump", (RtpCallback)rtp_session_resync, (long)NULL);
rtp_session_signal_connect(rtpr, "ssrc_changed", (RtpCallback)rtp_session_resync, (long)NULL);
return rtpr;
}

int main(int argc, char **argv)
{

ortp_init();
ortp_set_log_level_mask(ORTP_DEBUG|ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL);
ortp_scheduler_init();
OrtpEvQueue *q = ortp_ev_queue_new();

RtpSession *session = create_duplex_rtpsession(11111);
rtp_session_set_remote_addr(session, "127.0.0.1", 11111);

ms_init();
ms_load_plugins("/home/rudy/plugin");

struct mstream *stream;
stream = (struct mstream *)malloc(sizeof(struct mstream));

int sr = 8000;
int chan=1;
char *testfile1 = "rec.mp3";
char *testfile2 = "test.mp3";

stream->rtprecv = ms_filter_new(MS_RTP_RECV_ID);
stream->decrypto = ms_filter_new_from_name("Decrypt"); // 对应结构体MSFilterDesc中的name字段
stream->decoder = ms_filter_create_decoder("SPEEX");
stream->soundwrite = ms_filter_new( MS_FILE_REC_ID);

stream->soundread = ms_filter_new(MS_FILE_PLAYER_ID);
stream->crypto = ms_filter_new_from_name("Encrypt");// 对应结构体MSFilterDesc中的name字段
stream->encoder = ms_filter_create_encoder("SPEEX");
stream->rtpsend=ms_filter_new(MS_RTP_SEND_ID);

ms_filter_call_method(stream->soundwrite,MS_FILE_REC_OPEN,testfile1);
ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_SAMPLE_RATE,&sr);
ms_filter_call_method_noarg(stream->soundwrite,MS_FILE_REC_START);
ms_filter_call_method(stream->decoder,MS_FILTER_SET_SAMPLE_RATE,&sr);
ms_filter_call_method(stream->soundwrite,MS_FILTER_SET_NCHANNELS, &chan);
ms_filter_call_method(stream->rtprecv, MS_RTP_RECV_SET_SESSION, session);

ms_filter_call_method(stream->soundread,MS_FILE_PLAYER_OPEN,testfile2);
ms_filter_call_method(stream->encoder,MS_FILTER_SET_SAMPLE_RATE,&sr);
ms_filter_call_method(stream->rtpsend, MS_RTP_SEND_SET_SESSION, session);

ms_filter_link(stream->soundread, 0, stream->encoder, 0);//输入
ms_filter_link(stream->encoder, 0, stream->crypto, 0);//编码后进行加密
ms_filter_link(stream->crypto, 0, stream->decrypto, 0);//加密后进行解密

ms_filter_link(stream->decrypto, 0, stream->decoder, 0);//解密后进行解码
ms_filter_link(stream->decoder, 0, stream->soundwrite, 0);//输出

stream->ticker = ms_ticker_new();

ms_filter_call_method_noarg(stream->soundread,MS_FILE_PLAYER_START);

ms_ticker_set_name(stream->ticker, "recv stream");
ms_ticker_attach(stream->ticker, stream->soundread);

usleep(990000000);

ms_filter_call_method_noarg(stream->soundwrite,MS_FILE_REC_STOP);
ms_filter_call_method_noarg(stream->soundwrite,MS_FILE_REC_CLOSE);
ms_exit();
return 0;
}

下面是实验结果附图:

mediastreamer示例代码分析

注意,上图中输出的各个函数名与MSFilterDesc结构体中定义的名字必须一一对应!!!

更多有关mediastreamer2二次开发的内容,请查看文章修改mediastreamer2库源代码实现linphone加密语音通信